У меня есть одна большая таблица, которую я бы хотел оптимизировать. Я использую сервер MS-SQL 2005. Я попытаюсь описать, как он используется, и если у кого-то есть предложения, я бы очень его оценил.
Таблица составляет около 400 ГБ, каждый день вставляется 100 миллионов строк и 1 миллион строк. Таблица содержит 8 столбцов, 1 столбца данных и 7 столбцов, используемых для поиска/упорядочения.
k1 k2 k3 k4 k5 k6 k7 d1
где
k1: varchar(3), primary key - clustered index, 10 possible values
k2: bigint, primary key - clustered index, total rows/10 possible values
k3: int, 10 possible values
k4: money, 100 possible values
k5: bool
k6: bool
k7: DateTime
Выполняется только один запрос выбора, который выглядит следующим образом:
SELECT TOP(g) d1 FROM table WITH(NOLOCK)
WHERE k1 = a
AND k3 = c
AND k4 = d
AND k5 = e
AND k6 = f
ORDER BY k7
где g = около 1 миллиона Этот запрос мы запускали около 10 раз в день (часто во время вставки) и занимает около 5-30 минут.
Итак, в настоящее время у меня есть только кластерный индекс в двух столбцах первичного ключа. Мой вопрос: какие индексы я должен добавить для улучшения производительности этого запроса?
Будет ли отдельный индекс для каждого столбца хорошим выбором? Я думаю, что один индекс займет около 5-8 ГБ. Сервер БД имеет общий объем оперативной памяти 8 ГБ.
Пожалуйста, не говорите, что лучше всего экспериментировать. Это сродни: "Я не знаю, работайте над собой":)
Любые полезные советы!
EDIT by doofledorfer -
Вы вызвали вспышку преждевременной оптимизации здесь, если не откровенные предложения, что "лучше всего экспериментировать". Вам нужно прояснить ряд проблем, если вам нужна полезная помощь.
- doofledorfer
EDIT: Комментарии к сообщениям на сегодняшний день теперь размещены ниже вместе с планом запроса - Г-н Флиббл
Вероятно, вы связаны с интерфейсом ввода/вывода
Да, это не связано с ЦП. Высокий уровень доступа к диску. Кажется, что используется вся оперативная память. Используется ли оно мудро или нет, пока не видно.
Вы говорите, что не можете разделить данные, потому что используются все данные: НЕВОЗМОЖНО
Я имею в виду, что все данные используются в какой-то момент - не все данные используются каждым пользователем в каждом запросе. Я могу разделить данные, но пока не понимаю, почему разбиение таблицы лучше, чем использование кластерного индекса.
Почему вы выбрали эти типы Вероятно, VARCHAR должен был быть INT, поскольку это может быть только несколько значений. Остальные достаточно разумны, деньги представляют собой денежную ценность в реальной жизни, а bigint - это идентификатор, а bools - onny, offy type things:)
По какой-либо причине мы могли бы получить представление инструкции insert, или TSQL или объемный файл
TSQL. Его в основном INSERT INTO таблицы VALUES (k1, k2, k3, k4, k5, k6, d1). Единственное, что интересно, это то, что много дубликатов вставок предпринимаются, и ограничение k1 и k2 PK используется для предотвращения дублирования данных, поступающих в базу данных. Я верил во время разработки (и теперь), что это было так же быстро, как любой, чтобы уничтожить дублированные данные.
Можете ли вы сказать, как часто ваша вставка происходит Каждые 10 минут или около того вставки запускаются (ADO.NET), возможно, 10K за раз и занимают несколько минут. По моим оценкам, в настоящее время полные дневные вставки занимают 40% времени в день.
Поле DateTime содержит дату вставки Нет. На самом деле есть другой столбец DateTime, но он не извлекается ни в одном запросе SELECT, поэтому я просто не упомянул об этом ради простоты.
Как вы пришли к этому Еще один человек думает.
Если вас интересуют только последние данные, удаление/архивирование бесполезных данных может иметь смысл (начинайте с нуля каждое утро)
Мне не интересны только последние данные. Запрос может выбрать некоторые из самых первых данных, которые были вставлены в таблицу, вплоть до данных, вставленных минут назад. Но поскольку данные отфильтрованы, это не означает, что все данные в БД запрашиваются в этом запросе.
если имеется только один "вставщик" и только один "читатель", вы можете переключиться на специализированный тип (hashmap/list/deque/stack) или что-то более развитое на языке программирования.
Наверное, я буду придерживаться MSSQL. Он еще не сломался, только немного медленно.
liggett78, вы предлагаете кластеризованный индекс для столбцов k1, k4, k5, k6, k3 или некластеризованный индекс в этих столбцах?
Теперь мой главный вопрос: я должен расширить текущий кластеризованный индекс, чтобы также содержать k4 (это col со следующими наиболее вероятными значениями) или просто добавить некластеризованный индекс в k4.
Добавил бы все k1-k6 в кластеризованный индекс? Затем для отдельного некластеризованного индекса в столбце DateTime для ORDER BY? Правильно ли я полагаю, что это не приведет к существенному увеличению размера БД, а повлияет только на время вставки. Может ли кто-нибудь оценить эффект, который это будет иметь на вставках?
Я думаю, что если добавление индексов ко всем столбцам удваивает размер БД, то это небезопасно без больших (т.е. аппаратных) изменений.
Следующий план был запущен с индексом (не кластеризованным) в столбце DATE.
EDIT: Не уверен, что вы можете увидеть XML ниже, так что вот ссылка на него: http://conormccarthy.com/box/queryplan.sqlplan.txt
<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.0" Build="9.00.1399.06" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementCompId="1" StatementEstRows="11111" StatementId="1" StatementOptmLevel="FULL" StatementSubTreeCost="625.754" StatementText="SELECT TOP(11111) d1 FROM hands WITH (NOLOCK) 
 WHERE k4 = '10' 
 AND k6 = 1 
 AND k5 = 1 
 AND k1 = 'IPN' 
 AND k3 BETWEEN 2 AND 10 
 ORDER BY k7 DESC

" StatementType="SELECT">
<StatementSetOptions ANSI_NULLS="false" ANSI_PADDING="false" ANSI_WARNINGS="false" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="false" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="false" />
<QueryPlan DegreeOfParallelism="1" CachedPlanSize="36">
<MissingIndexes>
<MissingIndexGroup Impact="81.7837">
<MissingIndex Database="[MYDB]" Schema="[dbo]" Table="[Hands]">
<ColumnGroup Usage="EQUALITY">
<Column Name="[k1]" ColumnId="1" />
<Column Name="[k4]" ColumnId="7" />
<Column Name="[k5]" ColumnId="9" />
<Column Name="[k6]" ColumnId="10" />
</ColumnGroup>
<ColumnGroup Usage="INEQUALITY">
<Column Name="[k3]" ColumnId="6" />
</ColumnGroup>
<ColumnGroup Usage="INCLUDE">
<Column Name="[d1]" ColumnId="3" />
<Column Name="[k7]" ColumnId="4" />
</ColumnGroup>
</MissingIndex>
</MissingIndexGroup>
</MissingIndexes>
<RelOp AvgRowSize="75" EstimateCPU="0.0011111" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="11111" LogicalOp="Top" NodeId="0" Parallel="false" PhysicalOp="Top" EstimatedTotalSubtreeCost="625.754">
<OutputList>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="d1" />
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="0" ActualRows="11111" ActualEndOfScans="1" ActualExecutions="1" />
</RunTimeInformation>
<Top RowCount="false" IsPercent="false" WithTies="false">
<TopExpression>
<ScalarOperator ScalarString="(11111)">
<Const ConstValue="(11111)" />
</ScalarOperator>
</TopExpression>
<RelOp AvgRowSize="83" EstimateCPU="135.557" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="11111" LogicalOp="Filter" NodeId="1" Parallel="false" PhysicalOp="Filter" EstimatedTotalSubtreeCost="625.753">
<OutputList>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="d1" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k7" />
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="0" ActualRows="11111" ActualEndOfScans="0" ActualExecutions="1" />
</RunTimeInformation>
<Filter StartupExpression="false">
<RelOp AvgRowSize="96" EstimateCPU="318.331" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="195691" LogicalOp="Inner Join" NodeId="2" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="625.404">
<OutputList>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="d1" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k7" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k3" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k4" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k5" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k6" />
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="0" ActualRows="341958" ActualEndOfScans="0" ActualExecutions="1" />
</RunTimeInformation>
<NestedLoops Optimized="false" WithOrderedPrefetch="true">
<OuterReferences>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k1" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="HandId" />
<ColumnReference Column="Expr1003" />
</OuterReferences>
<RelOp AvgRowSize="32" EstimateCPU="330.366" EstimateIO="790.88" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="195691" LogicalOp="Index Scan" NodeId="4" Parallel="false" PhysicalOp="Index Scan" EstimatedTotalSubtreeCost="2.88444">
<OutputList>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k1" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="HandId" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k7" />
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="0" ActualRows="341958" ActualEndOfScans="0" ActualExecutions="1" />
</RunTimeInformation>
<IndexScan Ordered="true" ScanDirection="BACKWARD" ForcedIndex="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k1" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="HandId" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k7" />
</DefinedValue>
</DefinedValues>
<Object Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Index="[ix_dateplayed]" />
<Predicate>
<ScalarOperator ScalarString="[MYDB].[dbo].[Hands].[k1]=N'IPN'">
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k1" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="N'IPN'" />
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
</IndexScan>
</RelOp>
<RelOp AvgRowSize="88" EstimateCPU="0.0001581" EstimateIO="0.003125" EstimateRebinds="195691" EstimateRewinds="0" EstimateRows="1" LogicalOp="Clustered Index Seek" NodeId="6" Parallel="false" PhysicalOp="Clustered Index Seek" EstimatedTotalSubtreeCost="621.331">
<OutputList>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="d1" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k3" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k4" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k5" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k6" />
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="0" ActualRows="341958" ActualEndOfScans="0" ActualExecutions="341958" />
</RunTimeInformation>
<IndexScan Lookup="true" Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="d1" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k3" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k4" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k5" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k6" />
</DefinedValue>
</DefinedValues>
<Object Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Index="[PK_Hands]" TableReferenceId="-1" />
<SeekPredicates>
<SeekPredicate>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k1" />
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="HandId" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="[MYDB].[dbo].[Hands].[k1]">
<Identifier>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k1" />
</Identifier>
</ScalarOperator>
<ScalarOperator ScalarString="[MYDB].[dbo].[Hands].[HandId]">
<Identifier>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="HandId" />
</Identifier>
</ScalarOperator>
</RangeExpressions>
</Prefix>
</SeekPredicate>
</SeekPredicates>
</IndexScan>
</RelOp>
</NestedLoops>
</RelOp>
<Predicate>
<ScalarOperator ScalarString="[MYDB].[dbo].[Hands].[k4]=($10.0000) AND [MYDB].[dbo].[Hands].[k6]=(1) AND [MYDB].[dbo].[Hands].[k5]=(1) AND [MYDB].[dbo].[Hands].[k3]>=(2) AND [MYDB].[dbo].[Hands].[k3]<=(10)">
<Logical Operation="AND">
<ScalarOperator>
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k4" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="($10.0000)" />
</ScalarOperator>
</Compare>
</ScalarOperator>
<ScalarOperator>
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k6" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(1)" />
</ScalarOperator>
</Compare>
</ScalarOperator>
<ScalarOperator>
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k5" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(1)" />
</ScalarOperator>
</Compare>
</ScalarOperator>
<ScalarOperator>
<Compare CompareOp="GE">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k3" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(2)" />
</ScalarOperator>
</Compare>
</ScalarOperator>
<ScalarOperator>
<Compare CompareOp="LE">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[MYDB]" Schema="[dbo]" Table="[Hands]" Column="k3" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(10)" />
</ScalarOperator>
</Compare>
</ScalarOperator>
</Logical>
</ScalarOperator>
</Predicate>
</Filter>
</RelOp>
</Top>
</RelOp>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>