Влияние кластерного индекса на производительность БД - PullRequest
9 голосов
/ 20 июля 2010

Недавно я начал участвовать в новом программном проекте, в котором для хранения данных используется SQL Server 2000.

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

Sequence  numeric(18, 0)
Date      datetime
Client    varchar(9)
Hash      tinyint

В этой таблице много операций вставки в процессе нормальной работы.

Теперь я разработчик на C ++, а не администратор БД, но мое первое впечатление от этой схемы таблиц было то, что наличие этих полей в качестве кластеризованного индекса было бы очень пагубным для повышения производительности, поскольку данные должны физически переупорядочено на каждой вставке.

Кроме того, я не вижу в этом никакой пользы, поскольку нужно было бы часто запрашивать все эти поля, чтобы оправдать кластеризованный индекс, верно?

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

Ответы [ 5 ]

16 голосов
/ 21 июля 2010

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

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

Вы сказали

Кроме того, я не вижу в этом никакой пользы, поскольку можно было бычасто запрашивать все эти поля для обоснования кластеризованного индекса, верно?

Это не так.Поиск можно выполнить, просто используя первый или два столбца кластерного индекса.Это может быть дальний поиск, но это все еще поиск.Вам не нужно указывать все его столбцы, чтобы получить эту выгоду.Но порядок столбцов имеет большое значение.Если вы в основном обращаетесь к клиенту, то столбец «Последовательность» является плохим выбором, поскольку он является первым в кластерном индексе.Выбор второго столбца должен быть элементом, который больше всего запрашивается в связи с первым (не сам по себе).Если вы обнаружите, что второй столбец запрашивается сам по себе почти так же часто, как и первый столбец, тогда поможет некластеризованный индекс.

Как уже говорили другие, сокращение числа столбцов / байтов в кластеризованном индексе на столько женасколько это возможно, важно.

Очень плохо, что последовательность является случайным значением, а не увеличивается, но это не поможет.Ответ не для добавления в столбец идентификаторов, если только ваше приложение не может начать использовать его в качестве основного условия запроса для этой таблицы (маловероятно).Теперь, поскольку вы застряли в этом столбце случайной последовательности (предположим, что он наиболее часто запрашиваемый), давайте посмотрим на другое из ваших утверждений:

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

Это не совсем так.

Физическое расположение на диске не совсем то, что мыречь идет здесь, но это вступает в игру с точки зрения фрагментации, которая является показателем производительности.

Строки внутри каждой 8k-страницы не упорядочены.Просто все строки на каждой странице меньше, чем на следующей странице, и больше, чем на предыдущей.Проблема возникает, когда вы вставляете строку и страница заполнена: вы получаете разделение страницы.Движок должен скопировать все строки после вставленной строки на новую страницу, и это может быть дорого.Со случайным ключом вы получите много расколов страницы.Вы можете улучшить проблему, используя меньший коэффициент заполнения при перестройке индекса.Вам нужно поиграть с ним, чтобы получить правильный номер, но 70% или 60% могут служить вам лучше, чем 90%.

Я считаю, что использование даты и времени в качестве второго столбца CI может быть полезным, так как выВы по-прежнему будете иметь дело со страницами, которые нужно разделить между двумя различными значениями Sequence, но это не так плохо, как если бы второй столбец в CI также был случайным, так как вы гарантированно разбивали страницы при каждой вставке, где свозрастающее значение, вам может повезти, если строку можно добавить на страницу, потому что следующий порядковый номер начинается на следующей странице.

Сокращение типов данных и количества всех столбцов в таблице, а также ее некластеризованногоИндексы также могут повысить производительность, так как больше строк на страницу = меньше чтений страниц за запрос.Особенно, если двигатель вынужден делать сканирование таблицы.Перемещение группы редко запрашиваемых столбцов в отдельную таблицу 1-1 может творить чудеса с некоторыми вашими запросами.

Наконец, есть некоторые изменения в дизайне, которые также могут помочь (на мой взгляд):

  • Измените столбец Sequence на bigint, чтобы сохранить байт для каждой строки (8 байтов вместо 9 для числового значения).
  • Используйте справочную таблицу для Клиента с 4-байтовым столбцом идентификации int вместо varchar (9).Это экономит 5 байтов на строку.Если возможно, используйте smallint (от -32768 до 32767), который составляет 2 байта, что еще больше экономит 7 байтов на строку.

Сводка: CI должен начинаться с столбца, к которому чаще всего обращаются.Удалите все столбцы из CI, которые вы можете.Укоротите столбцы (байты) как можно больше.Используйте более низкий коэффициент заполнения, чтобы смягчить разрывы страниц, вызванные случайным столбцом «Последовательность» (если он должен оставаться первым из-за наибольшего количества запросов).

О, и начните выполнять дефрагментацию в Интернете.Если таблицу нельзя изменить, по крайней мере, ее можно часто реорганизовывать, чтобы поддерживать ее в наилучшей возможной форме.Также не пренебрегайте статистикой, поэтому движок может выбрать соответствующие планы выполнения.

UPDATE

Еще одна стратегия, которую следует рассмотреть, - это если составной ключ, используемый в таблице, можетбыть преобразован в int, и таблица поиска значений создается.Допустим, некоторая комбинация из менее чем 4 столбцов повторяется в более чем 100 строках, например, Sequence + Client + Hash, но только с различными значениями Date.Тогда вставка в отдельную таблицу SequenceClientHash со столбцом идентификаторов может иметь смысл, потому что тогда вы можете один раз найти искусственный ключ и использовать его снова и снова.Это также заставит ваш CI добавлять новые строки только на последней странице (yay) и существенно уменьшит размер CI, как повторяется для всех некластеризованных индексов (yippee).Но это имело бы смысл только в определенных узких шаблонах использования.

Теперь marc_s предложил просто добавить дополнительный столбец с идентификационными данными int в качестве кластеризованного индекса.Возможно, это могло бы помочь, если бы все некластеризованные индексы получали больше строк на страницу, но все зависит от того, где именно вы хотите, чтобы производительность была, потому что это гарантировало бы, что каждый запрос таблицы должен использовать закладку.поиск, и вы никогда не сможете получить поиск по таблице.

О «тоннах разбиения страниц и плохой фрагментации индекса»: как я уже говорил, это можно несколько улучшить при более низком коэффициенте заполнения.Кроме того, частая реорганизация индекса в сети (не то же самое, что перестройка) может помочь уменьшить эффект от этого.

В конечном счете, все сводится к точной системе и ее уникальной схеме доступа к данным в сочетании с решениями о том, какие частиВы хотите оптимизировать.Для некоторых систем медленная вставка неплоха, если выбор всегда быстрый.Для других более важно иметь постоянное, но немного более медленное время выбора, чем немного более быстрое, но непоследовательное время выбора.Для других данные в действительности не читаются до тех пор, пока они не будут переданы в хранилище данных, поэтому вставки должны быть максимально быстрыми.И в добавление к этому добавляется тот факт, что производительность зависит не только от времени ожидания пользователя или даже времени ответа на запрос, но и от ресурсов сервера, особенно в случае массового параллелизма, так что общая пропускная способность (скажем, в ответах клиента за единицу времени)имеет значение больше, чем любой другой фактор.

5 голосов
/ 20 июля 2010

Кластерные индексы (CI) работают лучше, чем постоянно растущие, узкие, редко меняющиеся значения.Вы хотите, чтобы ваш CI охватывал столбцы, которые чаще всего попадают в запросы с операторами> =, <= или BETWEEN.</p>

Я не уверен, как ваши данные обычно попадают.Чаще всего вы видите CI в столбце IDENTITY или другом узком столбце (потому что этот столбец также будет возвращен «привязанным» ко всем некластеризованным индексам, и мы не хотим, чтобы тонна данных добавлялась к каждой выборкеесли это не нужно).Возможно, к данным чаще всего поступают запросы в срок, и это может быть хорошим выбором, но все четыре столбца вероятно не верны (я подчеркиваю, вероятно, потому что я не знаю настройкиэто может не иметь ничего плохого в этом).Здесь есть несколько указателей: http://msdn.microsoft.com/en-us/library/aa933131%28SQL.80%29.aspx

2 голосов
/ 20 июля 2010

Есть несколько вещей, которые вы неправильно понимаете, как SQL создает и использует индексы.

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

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

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

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

Итог: не оптимизируйте индексы для производительности вставки, пока вы действительно не обнаружите проблему производительности со вставками. Обычно это того не стоит.

1 голос
/ 20 июля 2010

Если у вас есть только один кластерный индекс в вашей таблице, это может быть не так уж плохо.Однако индекс кластеризации также используется для поиска страницы реальных данных на предмет любого попадания в некластеризованный индекс - поэтому кластерный индекс (все его столбцы) также являются частью каждого некластеризованного индекса, который вы можете иметь наваша таблица.

Итак, если у вас есть несколько некластеризованных индексов на вашей таблице, то вы определенно а) тратите много места (и не только на диске - также в оперативной памяти вашего сервера!), и b) ваша производительность будет плохой.

Хороший кластеризованный индекс должен быть:

  • маленький (лучшая ставка: 4-байтовый INT) - ваш довольно плохой - до 28байт на запись
  • уникальный
  • стабильный (никогда не меняется)
  • постоянно увеличивающийся

Держу пари, что ваша текущая установка нарушает по крайней мере два, еслине больше из этих требований.Несоблюдение этих рекомендаций приведет к пустой трате пространства, и, как вы правильно сказали, большое количество фрагментов страниц и индексов и разбиений страниц (необходимость «переупорядочивать» данные, когда вставка происходит где-то в середине кластерного индекса).

Честно говоря: просто добавьте суррогат ID INT IDENTITY(1,1) к вашей таблице и сделайте его первичным кластерным ключом - вы увидите довольно хороший прирост производительности, просто если у вас много операций INSERT (и UPDATE)Продолжение!

См. дополнительную справочную информацию о том, что делает хороший ключ кластеризации и что важно для них, здесь:

0 голосов
/ 09 августа 2010

В конечном итоге я согласен с последним абзацем Эрика:

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

Это основная вещь, которую я заставляю людей учиться: универсального решения не существует.

Вы должны знать свои данные и действия, выполненные против них. Вы должны знать, как часто встречаются различные типы действий, их влияние и ожидаемое время выполнения (вам не нужно жестко настраивать некоторые редко выполняемые запросы и влиять на все остальное, если конечный пользователь согласен, что время выполнения запроса не так важно - скажем, подождать несколько минут для отчета один раз в неделю - это нормально). Конечно, как сказал Эрик

«производительность зависит не только от времени ожидания пользователя или даже времени ответа на запрос, но и от ресурсов сервера»

Если такой запрос влияет на общую производительность сервера, его следует рассматривать как серьезного кандидата для оптимизации, даже если время выполнения хорошее. Я видел несколько очень быстрых запросов, которые использовали огромное количество ЦП на многопроцессорных серверах, в то время как несколько более медленное решение было несравненно «легче» с точки зрения использования ресурсов. В этом случае я почти всегда выбираю более медленный.

Когда вы знаете, какова ваша цель, вы можете решить, сколько индексов вам нужно и какой из них должен быть кластеризован. Уникальные ограничения, отфильтрованные индексы, индексы с включенными столбцами - довольно мощные инструменты для настройки. Выбор правильных столбцов важен, но часто выбор правильного порядка столбцов еще важнее. И, наконец, не убивайте производительность вставки / обновления с тоннами индексов, если таблица часто модифицируется.

...