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

169

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

Когда вы будете использовать NONCLUSTEREDпервичный ключ в отличие от CLUSTEREDпервичного ключа?

заранее спасибо

Стюарт Блэклер
источник

Ответы:

188

Вопрос не в том, «когда ПК должен быть NC», а в том, чтобы спросить «каков правильный ключ для кластерного индекса»?

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

Другая часть загадки - как можно использовать индекс ? Есть три типичных шаблона:

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

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

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

Хороший кластеризованный индекс стабилен , он не изменяется в течение всего времени существования объекта, поскольку изменение значений ключа кластеризованного индекса означает, что строка должна быть удалена и вставлена ​​обратно.

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

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

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

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

Ремус Русану
источник
1
Что здесь означает «упорядочить по требованиям, когда индекс может удовлетворить заказ, не требуя сортировки по принципу« останови и иди »»?
Майк Шеррилл 'Cat Recall'
2
@RemusRusanu. +1 Очень полезный ответ. Один вопрос по поводу примера (state, id). В этом примере требование «хороший кластерный индекс растет не в случайном порядке» не будет выполнено, не так ли? Так можно ли считать его хорошим кластерным индексом?
ЖЖ
26

Основная причина использования кластерных индексов указана в Википедии :

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

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

Таким образом, я всегда могу выбрать ГДЕ или ЗАКАЗАТЬ по столбцу Страна; кластерный индекс в первичном ключе не приносит мне никакой пользы, я не обращаюсь к этим данным по PK, я получаю к ним доступ через этот другой столбец. Поскольку у меня может быть только один кластеризованный индекс в таблице, объявление моего PK как кластеризованного помешало бы мне использовать кластеризованный индекс по стране.

Кроме того, здесь хорошая статья на кластерного против некластеризованных индексов , оказывается, кластерные индексы вызвали вставки проблемы производительности в SQL Server 6.5 (который , по крайней мере , надеюсь , это не имеет значения для большинства из нас здесь).

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

Обратите внимание, что это не так в более поздних версиях.

Бен Брока
источник
3
FIY, вы упомянули SQL Server 6.5: dba.stackexchange.com/questions/1584/...
ГБН
15

Если у вас первичный ключ UNIQUEIDENTIFIER, убедитесь, что он указан NONCLUSTERED. Если вы сделаете это кластеризованным, каждая вставка должна будет выполнить кучу перемешивания записей, чтобы вставить новую строку в правильное положение. Это будет производительность танка.

Брайан Джонс
источник
1
Хотя я пытаюсь избежать UUID для кластеризованных ключей, я полагаю, что приведенные выше рассуждения могут быть неполными. SQL-сервер не обязательно переставляет строки для вставки в правильную позицию (если вы имеете в виду «между более низким и более высоким значением»). Рассмотрим вставку в середину таблицы триллионов строк. Требуется дополнительная косвенность, которая может быть тем, что вы имели в виду. Последовательный UNIQUEIDENTIFIERтип также существует и имеет такую ​​же вероятность генерирования уникальных ключей, хотя он все еще страдает от размера 128.
Чарльз Бернс
8

Очень распространенный пример:

  • Customerстол с CustomerIDкакCLUSTERED PRIMARY KEY
  • Таблица заказов с OrderID (PK), CustomerID, OrderDateи некоторыми другими столбцами
  • OrderPositions с участием OrderPositionID (PK), OrderId, ProductID, Amount, Price ...
  • Вы должны проиндексировать таблицы заказов

Конечно, «это зависит» - как почти всегда - правильный ответ, но большинство приложений (не BI-отчеты) будут работать на основе клиента (например, вы входите в систему как пользователь 278 на веб-сайт и нажимаете «Мои заказы» или Клерк перечисляет все заказы для клиента 4569, или ваша процедура выставления счетов суммирует все заказы для клиента 137).

В этом случае не имеет смысла кластеризовать таблицу по OrderID. Да, у вас будут запросы SELECT ... WHERE OrderId = ?на перечисление деталей заказа, но обычно это короткий и дешевый (3 чтения) поиск индекса.

С другой стороны, если вы будете кластеризовать свою Orderтаблицу с помощью CustomerID, ей не нужно будет выполнять многократный поиск ключей каждый раз, когда вы запрашиваете таблицу CustomerId = ?.

Это CLUSTERED INDEXдолжно быть всегда UNIQUE, иначе SQL Server добавил бы невидимый (= неиспользуемый) столбец INT, UNIQUIFIERчтобы обеспечить уникальность, и было бы гораздо разумнее добавлять реальные (пригодные для использования) данные, чем некоторые случайные (в зависимости от порядка вставки) вещи.

Поскольку клиент (надеюсь) разместит более одного заказа, нам придется добавить либо OrderID(или (если вы обычно сортируете для этого)) OrderDate(если это дата-время - в противном случае клиент будет ограничен одним заказом в день) в CLUSTERED INDEXи в конечном итоге с:

CREATE UNIQUE CLUSTERED INDEX IX_Orders_UQ on Orders (CustomerID, OrderID)

Те же правила применяются к OrderPositionsтаблице. Обычно большинство запросов будут перечислены все позиции для на определенном порядке, так что вы должны создать ПК с , OrderPositionIDкак NONCLUSTEREDи UNIQUE CLUSTERED INDEXна OrderId, OrderPositionID.

Кстати, верно, что Customerтаблица кластеризована по ее PK ( CustomerIDпотому что она является «таблицей верхнего уровня» и будет - в типичном приложении - в основном запрашиваться ее CustomerID.

Таблицы чистого просмотра, например, Gendersили, InvoiceTypesили PaymentTypeявляются еще одним примером таблиц, которые должны быть сгруппированы по их PK (потому что вы обычно присоединяетесь к ним GenderId, InvoiceTypeIdили PaymentTypeId).

Томас Франц
источник
2

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

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

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

crokusek
источник