У меня есть одна большая таблица, которую я хотел бы оптимизировать.
Я использую сервер 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
где г = около 1 миллиона
Этот запрос выполнялся около 10 раз в день (часто во время вставок) и занимает около 5-30 минут.
Так что в настоящее время у меня есть только кластерный индекс для двух столбцов первичного ключа. У меня вопрос: какие индексы я должен добавить, чтобы улучшить производительность этого запроса?
Будут ли хорошим выбором отдельные индексы для каждого столбца? Я думаю, что один индекс будет занимать около 5-8 ГБ. Всего на сервере БД 8 ГБ ОЗУ.
Пожалуйста, не говорите, что лучше всего экспериментировать. Это похоже на «я не знаю, разберись сам»:)
Любые советы очень ценятся!
РЕДАКТИРОВАТЬ doofledorfer -
Вы вызвали здесь преждевременную оптимизацию, если не откровенные предположения, что «лучше всего экспериментировать». Вам нужно уточнить ряд вопросов, если вам нужна полезная помощь.
- doofledorfer
РЕДАКТИРОВАТЬ: комментарии к сообщениям на сегодняшний день размещены ниже вместе с планом запроса
- Мистер Флиббл
Вы, вероятно, связаны с вводом / выводом
Да, он не связан с процессором. Доступ к диску высокий. Вся доступная оперативная память, кажется, используется. Мудро ли оно используется или нет, еще неизвестно.
Вы говорите, что не можете разделить данные, потому что используются все данные: НЕВОЗМОЖНО
Я имею в виду, что все данные используются в какой-то момент, а не то, что все данные используются каждым пользователем в каждом запросе.
Конечно, я могу разделить данные, но пока не понимаю, почему разбиение таблицы лучше, чем использование кластерного индекса.
Почему вы выбрали эти типы
VARCHAR, вероятно, должен был быть INT, поскольку это может быть только несколько значений. Остальные достаточно разумны, деньги представляют собой денежную ценность в реальной жизни, а bigint - это удостоверение личности, а bool - вещи типа «однодневка»:)
Мы можем случайно взглянуть на оператор вставки или TSQL или bulkinsert
TSQL. В основном это INSERT INTO Table VALUES (k1, k2, k3, k4, k5, k6, d1). Единственное, что в любом случае интересно, это то, что делается много попыток вставки дубликатов, а ограничение PK k1 & k2 используется для предотвращения попадания дубликатов данных в базу данных. Во время разработки (и сейчас) я полагал, что это был такой же быстрый способ, как и любой другой, найти повторяющиеся данные.
Можете ли вы сказать, как часто происходит вставка
Каждые 10 минут или около того запуска вставок (ADO.NET), может быть, 10 КБ за раз и занимает несколько минут. По моим оценкам, в настоящее время вставки полного дня занимают 40% времени дня.
Содержит ли поле DateTime дату вставки
Нет. На самом деле есть еще один столбец DateTime, который не используется ни в одном из запросов SELECT, поэтому я не упомянул его для простоты.
Как ты пришел к этому
Больше одного дня человек думает.
если вас интересуют только последние данные, удаление / архивирование ненужных данных может иметь смысл (начинайте каждое утро с нуля)
Меня не интересуют только последние данные. Запрос может выбрать некоторые из самых первых данных, которые были вставлены в таблицу, вплоть до данных, вставленных несколько минут назад. Но поскольку данные фильтруются, это не означает, что в этом запросе запрашиваются все данные в БД.
если есть только один «инсертор» и только один «ридер», вы можете переключиться на специализированный тип (hashmap / list / deque / stack) или что-то более сложное на языке программирования.
Я, вероятно, буду придерживаться MSSQL на данный момент. Это еще не сломано, только немного медленно.
liggett78, вы предлагаете кластерный индекс для столбцов k1, k4, k5, k6, k3 или некластеризованный индекс для этих столбцов?
Мой главный вопрос сейчас заключается в том, должен ли я расширить текущий кластеризованный индекс, чтобы он также содержал k4 (это столбец со следующими наиболее возможными значениями), или мне просто добавить некластеризованный индекс в 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>