Одна таблица 400 ГБ, один запрос - нужны идеи настройки (SQL2005) - PullRequest
13 голосов
/ 08 декабря 2008

У меня есть одна большая таблица, которую я хотел бы оптимизировать. Я использую сервер 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) &#xD;&#xA;                                WHERE k4 = '10' &#xD;&#xA;                                AND k6 = 1 &#xD;&#xA;                                AND k5 = 1  &#xD;&#xA;                                AND k1 = 'IPN'  &#xD;&#xA;                                AND k3 BETWEEN 2 AND 10  &#xD;&#xA;                                ORDER BY k7 DESC&#xD;&#xA;&#xD;&#xA;" 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]&gt;=(2) AND [MYDB].[dbo].[Hands].[k3]&lt;=(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>

Ответы [ 24 ]

1 голос
/ 09 декабря 2008

Разделение и распараллеливание - проверьте план запроса, если он не показывает, что запрос распараллелен, то выясните, почему это не так. Возможно, вам придется разбить запрос на пару шагов, а затем объединить результаты.

Если это затем разделить данные на несколько физических дисков, добавьте больше ядер. У него много работы, после того, как вы извлечете из этого чертову массу, физическая сила - это все, что осталось.

Не думайте, что SQL Server будет просто использовать все ваши ядра. Как правило, вы должны правильно составить запрос, чтобы можно было использовать несколько ядер. Проверьте свойства первого узла в плане запроса, чтобы увидеть DOP (степень параллелизма). Если его 1, вы тратите впустую ядра ...

1 голос
/ 20 июня 2009

Похоже, что вы не используете свой кластеризованный индекс в полной мере и имеете много дублированных данных.

Ваш кластеризованный индекс выглядит примерно так:

create clustered index IX_Clustered on Table(k1 ASC, k2 ASC)

Однако остальные ваши столбцы k * представляют только 40 000 возможных перестановок.

10 (k1) * 10 (k3) * 100 (k4) * 2 (k5) * 2 (k6) = 40000

Вы должны вытащить уникальные комбинации этих 4 ключей в отдельную таблицу и дать каждому из них уникальный int (первичный ключ "newPK").

Извините за псевдокод, пожалуйста:

create table SurrogateKey(
  newPK int -- /*primary key*/
, k1, k3, k4, k5, k6
)

constraint: newPK is primary key, clustered
constraint: k1, k3, k4, k5, k6 is unique

Эта таблица будет содержать только 40 000 строк и будет очень быстро искать первичный ключ, newPK. Затем вы можете найти одно целое число в вашей большой таблице.

Ваша существующая таблица должна быть изменена, чтобы иметь следующие столбцы:

  • newPK
  • k2 (который на самом деле не является ключом, вероятно, просто порядковым номером)
  • d1
  • k7 дата / время

Учитывая вышеизложенное, вы можете изменить свой кластеризованный индекс на:

create clustered index IX_Clustered on Table(newPK ASC)

И вы можете искать по этому пути. Гарантируется, что он будет быстрее, чем ваш запрос (эквивалентная производительность для сканирования по индексу + поиск по ключу).

declare @pk int
select @pk = newPK 
from SurrogateKey
where
      k1 = @k1
  and k3 = @k3
  and k4 = @k4
  and k5 = @k5
  and k6 = @k6

select top(g1) d1, k2, k7
from Table with(read uncommitted)
where newPK = @pk
order by k7

Ваш оператор вставки также необходимо изменить, чтобы запросить / вставить таблицу SurrogateKey.

1 голос
/ 08 декабря 2008

Показать вывод плана запроса - любое приключение по настройке, которое не начинается, является несчастным случаем.

0 голосов
/ 12 декабря 2008

что такое D1, десятичный или длинный символ, пожалуйста, не могли бы вы уточнить это? Я рекомендую создать кластерный индекс как (K7, k2, k1, k4), а затем создать дополнительный индекс для (k3) (создание индекса по двум значениям bool в большинстве случаев не имеет смысла, если распределение значений не составляет около 30% / 70% между значениями, или если ваша таблица очень широка, если d1).

это изменение не сильно повлияет на скорость вставки, а даст вам приблизительный общий ответ на кластеризованный индекс.

0 голосов
/ 09 декабря 2008

Я думаю, что кластерный индекс на K7 - единственная вещь любого значения. Остальная часть предложения where имеет такую ​​низкую избирательность, что это пустая трата времени.

Если вы не можете воспользоваться какими-то конкретными знаниями о ваших значениях (возможно, k5 верна только, если k4 <0 или что-то в этом роде), вы в значительной степени смотрите на сканирование кластерного индекса. Можно также сделать это поле, по которому вы заказываете. </p>

Глядя на небольшое количество различных значений в k3 - k6, вам, вероятно, потребуется только прочитать <1,5 миллиона строк, чтобы получить свой верхний миллион. Это, вероятно, лучшее, что вы собираетесь сделать, тем более что любой другой план потребует, чтобы вы все равно заказали по k7, чтобы оценить ваше предложение TOP. </p>

0 голосов
/ 08 декабря 2008

Вам необходимо создать индекс, который уменьшит количество возможных строк, возвращаемых как можно быстрее.

Следовательно, самый простой индекс для создания будет в столбце k4, так как он может иметь наибольшее количество различных значений. Необходимо только индексировать начальную подстроку k4, где ожидаемые значения k4 отличаются в пределах этой подстроки. Это уменьшит размер индекса и ускорит доступ.

k7 также следует проиндексировать, поскольку это значительно увеличит скорость предложения orderby.

Вам также может понадобиться экспериментировать (я знаю, я знаю, вы сказали, что не экспериментируйте, но это может помочь ...) с созданием индекса из нескольких столбцов в следующем порядке: k4, k1, k2, k3 Это, опять же, должно уменьшить число возможных строк, возвращаемых как можно быстрее.

0 голосов
/ 09 декабря 2008

Звучит как весело.

Несколько вопросов:

  • Почему вы выбрали эти типы? varchar, деньги, bigint, int, bool? есть ли причина или просто желание добавить немного веселья?
  • Каким-то образом мы могли бы взглянуть на оператор вставки, или TSQL, или bulkinsert?
  • Можете ли вы сказать, как часто происходит вставка (массовая или случайная?)
  • Содержит ли поле DateTime дату вставки?
  • Как вы к этому пришли? (один человек / день думает или команда из 20 человек работает безумно в течение последних трех месяцев?)

Мне кажется важным несколько фактов:

  • Вы вставляете миллионную строку каждый день
  • Вы хотите только последний миллион данных

Несколько замечаний пришло ко мне:

  • если вас интересуют только последние данные, удаление / архивирование ненужных данных может иметь смысл (начинайте каждое утро с нуля)
  • если есть только один «инсертор» и только один «ридер», вы можете переключиться на специализированный тип (hashmap / list / deque / stack) или что-то более сложное на языке программирования.
0 голосов
/ 09 декабря 2008

Ваш план запроса в основном показывает следующее:

  • Первая операция - это поиск кластеризованного индекса со сравнениями по k1, handId ?, d1, k3-k6
  • Во-вторых, это полное сканирование индекса на k1, handId? и к7
  • В-третьих, конечно, объединение для создания окончательного набора результатов
  • Сортировка ЗАКАЗАТЬ ПО
  • TOP n (Фильтр)

В плане предлагается индекс, который должен улучшить показатели химической завивки на 81% - k1, k4, k5, k6, k3 + включают d1 и k7. Я не знаю, сколько времени потребуется, чтобы построить такой индекс и увидеть результаты, но, как я здесь прокомментировал, он фактически удвоит размер вашей таблицы просто потому, что почти каждый столбец присутствует в индексе. Также вставки будут медленнее.

Как и предполагали многие, разделение - это лучшая стратегия, например. например, чтобы одна таблица имела значения k3 от 1 до 3, другая от 4 до 7 и третья от 8 до 10. В SQL Server Enterprise разделение выполняется с использованием ограничения CHECK для этого столбца, оптимизатор запросов определит, какую таблицу вывести из таблицы. n для сканирования / поиска в зависимости от значения параметра для столбца.

0 голосов
/ 08 декабря 2008

Вот идея, что если вы создадите вторую таблицу со всеми значениями Lookup, а затем вместо использования where вы объедините таблицы и выполните предложение where в новой таблице Lookup.

Также я думаю, что это может помочь, если вы разместите несколько строк данных и пример запроса, если это возможно.

0 голосов
/ 09 декабря 2008

Спасибо всем за помощь.

Я сделал 3 исправления ошибок в оригинальном сообщении.

1) ГДЕ должны были быть AND.

2) k4 должен был быть ДЕНЬГИ, а не VARCHAR. Кроме того, k1 имеет длину 3.

3) К2 не должно быть в предложении WHERE. Как правильно указывает doofledorfer, не имеет смысла иметь какие-либо другие операторы WHERE, кроме полного первичного ключа.

Вот ответы на ваши вопросы:

Почему вы сгруппированы по первичному ключу?

У меня сложилось впечатление, что по умолчанию в качестве кластерного индекса был задан PK. Я не менял его.

Какие столбцы могут быть NULL?

None.

Какова длина VARCHAR?

Я ошибся с типами столбцов. Единственный оставшийся VARCHAR имеет длину 3.

Что теперь дает план запроса?

Опубликовано в следующем посте.

Помогите мне больше узнать о столе. если ваш ПК k1, k2, вам не нужно выбирать любой другой столбец, чтобы получить совершенно уникальную запись. Это была ошибка. Часть k2 PK отсутствует в предложении WHERE.

Зная, почему вам нужно около миллиона записей Возвращение может помочь мне найти лучшее решение.

База данных содержит ежедневные записи (столбец d1 TEXT) или данные. Людям необходим доступ к большим объемам этих данных для составления собственных отчетов. Им нужно отфильтровать его по количеству значений и отсортировать по времени.

Похоже, вы хотите только самые ранние записи "g"? Может быть, только самые последние записи "g"?

Да, самое последнее. Но я определенное их количество. Я не знаю дату начала заранее.

У вас есть внешние ключи на k3, k4? Нет. Это единственная таблица в БД.

Комментарии:

Даже если кластеризованный индекс правильный, более селективное поле должно стоять первым.

Более селективный индекс не используется в предложении WHERE (после редактирования!). Я так понимаю, что в этом случае не должно быть на первом месте?

Возможно, вы захотите переместить данные определенного возраста в таблицу истории

В настоящее время используются все данные, поэтому обрезка не возможна.

Возможно, вы захотите дефрагментировать индекс

В настоящее время у меня нет ни одного. Посмотрим на это, если эта тема окажется плодотворной.

Добавить один индекс со столбцами k1-k6 в нем; это должно быть лучшим.

Может ли кто-нибудь еще прокомментировать это предложение? Liggett78 прокомментировал, что это удвоит размер БД, не сильно помогая из-за сортировки столбца даты. Обратите внимание, что столбец DATE отсутствует в предложении WHERE, он используется только для упорядочения данных.

Попробуйте превратить k1, k2 в целые и делая их внешние ключи, он будет использовать намного меньше памяти для одного, я бы думал, и я думаю, что это должно быть быстрее (хотя я могу ошибаться там, Я думаю, SQL Server кеширует эти значения).

k2 - это bigint (ошибка в оригинальном сообщении). Таким образом, замена k1 на int (из VARCHAR (3)) является опцией. Мы действительно думаем, что это будет иметь большое значение. И действительно ли люди думают, что разделение таблицы на k1, k2, d1 и k1, k2, k3, k4, k5, k7 и использование внешних ключей улучшит ситуацию?

Один хороший совет для улучшения скорости запросов это поместить в подзапрос, который сокращает уменьшите размер набора записей до управляемый. Там может быть некоторый набор данных, который немедленно сокращает набор записей, скажем, от 10 миллионов строк, до 10000.

например. ВЫБРАТЬ ТОП (г) d1 ОТ (ВЫБРАТЬ * ИЗ таблицы ГДЕ k1 = a С (NOLOCK)) ГДЕ И К3 = С И К4 = d И k5 = e И k6 = f ПОРЯДОК ПО К7

Очень интересно. Это действительно поможет? Кажется, что SQL Server был бы очень глупым, если бы он сам не урезал данные подобным образом.

Возможно, это время, затраченное на ваш пользовательский интерфейс или возможно ли отображать данные, возможно это время, затраченное Сетью?

Нет пользовательского интерфейса. Конечно, существуют проблемы с сетью при перемещении данных, но меня интересует только время, затраченное на запрос, чтобы начать возвращать результаты (я использую устройство чтения данных ADO.NET) в данный момент - по одному за раз:)

.. [чтобы] увидеть наибольшую выгоду ... разбить таблицу

Не будет ли кластеризованный индекс иметь такой же эффект?

Оставьте свой первичный ключ в покое, но создать кластерный индекс на вашу дату столбец, так как это то, что вы используете в СОРТИРОВАТЬ ПО. Таким образом, движок базы данных начал бы сканировать кластерный ключ, сравнить столбцы с поставляемым значения и выходные строки, которые удовлетворяют условия.

Звучит как звуковой план! Любые другие покровители?

Чтобы суммировать предложения:

1) Создайте отдельные индексы для всех ключей: большинство людей проголосуют за это?

2) Создание отдельных индексов для ключей с наиболее различимыми значениями.

3) Создайте многостолбцовый индекс для некоторых столбцов, в первую очередь для столбцов с самыми разными значениями.

4) Кидаем в нее ОЗУ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...