Должен ли индекс по столбцу идентификаторов быть некластеризованным?

19

Для таблицы со столбцом идентификаторов следует ли создавать кластерный или некластеризованный индекс PK / уникальный для столбца идентификаторов?

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

create table T (
  Id int identity(1,1) primary key, -- clustered or non-clustered? (surrogate key, may be used to join another table)
  A .... -- A, B, C have mixed data type of int, date, varchar, float, money, ....
  B ....
  C ....
  ....)

create index ix_A on T (A)
create index ix_..... -- Many indexes can be created for queries

-- Common query is query on A, B, C, ....
select A, B 
from T 
where A between @a and @a+5 -- This query will have less LIO if the PK is non-clustered (seek)

select A, B, C
from T 
where B between @a and @a+5 

....

Кластерный PK в столбце идентификаторов хорош, потому что:

  1. Монотонно увеличивается, поэтому при вставке страницы не разбиваются. Говорят, что массовая вставка может быть такой же быстрой, как и в кучной (некластеризованной) таблице.

  2. Это узко

Тем не менее, будут ли запросы в вопросе выполняться быстрее без настройки кластеризации?

** Обновление: ** Что если IdFK других таблиц и он будет объединен в некоторых запросах?

u23432534
источник
3
Это не лучше и не хуже, это зависит.
Аарон Бертран
1
@ypercube Ссылка kejser.org/clustered-indexes-vs-heaps говорит, что у не-CI будет меньше LIO.
u23432534
2
Я читал статью в прошлом, и она, безусловно, указывает, что есть случаи для кластеризованного индекса и случаи для кучи. Это не все черное или все белое.
ypercubeᵀᴹ
4
Я не уверен, что ваш ответ на @ypercube удовлетворяет какому-либо критерию, указанному г-ном Кейзером, - по крайней мере, с подробностями, которые вы поделились. В его нынешней форме я не уверен, что это даст полезный ответ, потому что он должен охватывать почти каждый сценарий - что уже сделано в сообщении в блоге, которое вы цитировали. Если вы можете предоставить более подробную информацию о вашем конкретном сценарии, то, возможно, некоторые знания в этой статье могут быть применены.
swasheck
2
Это будет зависеть от таких вещей, как: а) рабочая нагрузка (OLTP, OLAP, и т. Д.), Б) размер (ы) таблиц, в) нормальная форма, и это лишь некоторые из них. Вы не предоставили подробную информацию о каком-либо из этих факторов, поэтому любая рекомендация будет основываться на предположениях вашей среды. Кроме того, пытались ли вы профилировать запросы, которые вы предлагаете (с очищенными буферами), и получать конкретные профили ввода-вывода для каждой конфигурации и видеть сами?
swasheck

Ответы:

16

По умолчанию ПК кластеризован, и в большинстве случаев это нормально. Однако, какой вопрос следует задать:

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

PK и Clustered index являются двумя отличиями:

  • ПК является ограничением. PK используется для уникальной идентификации строк, но нет понятия хранения. Однако по умолчанию (в SSMS) он применяется уникальным кластерным индексом, если кластерный индекс еще не представлен.
  • Кластерные индексы - это особый тип индекса, который хранит данные строк на уровне листа, то есть он всегда покрывает. Все столбцы, являются ли они частью ключа или нет, хранятся на уровне листа. Он не обязательно должен быть уникальным, и в этом случае к кластеризованному ключу добавляется уникальный код (4 байта).

Теперь у нас 2 вопроса:

  • Как я хочу уникально идентифицировать строки в моей таблице (PK)
  • Как я хочу сохранить его на уровне листа индекса (Clustered Index)

Это зависит от того, как:

  • вы разрабатываете свою модель данных
  • вы запрашиваете ваши данные, и вы пишете свои запросы
  • Вы вставляете или обновляете свои данные
  • ...

Во-первых, вам нужен кластерный индекс? При массовой вставке более эффективно хранить неупорядоченные данные в HEAP (по сравнению с упорядоченными данными в кластере). Он использует RID (идентификатор строки, 8 байт) для уникальной идентификации строк и сохранения их на страницах.

Кластерный индекс не должен быть случайным значением. Данные на уровне листа будут сохранены и упорядочены по ключу индекса. Поэтому он должен постоянно расти, чтобы избежать фрагментации или разбиения страницы. Если это не может быть достигнуто PK, вы должны рассмотреть другой ключ в качестве кластеризованного кандидата. Кластерный индекс для одинаковых столбцов, последовательный идентификатор GUID или даже что-то вроде даты вставки - это хорошо с последовательной точки зрения, поскольку все строки будут добавлены на последнюю конечную страницу. С другой стороны, хотя уникальный идентификатор может быть полезен для вашего бизнеса в качестве PK, их не следует кластеризовывать (они упорядочены / сгенерированы случайным образом).

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

Ключ кластеризованного индекса состоит из всех столбцов, которые вы хотите проиндексировать. Столбец uniquefier (4 байта) добавляется, если на него нет уникального ограничения (инкрементное значение для дубликатов, в противном случае - ноль). Этот ключ индекса будет сохранен один раз для каждой строки на уровне листьев всех ваших некластеризованных индексов. Некоторые из них также будут храниться несколько раз на промежуточных уровнях (ветвях) между корнем и уровнем листьев дерева индексов (B-дерево). Если ключ слишком большой, все некластеризованные индексы станут больше, потребуется больше памяти и больше ввода-вывода, процессора, памяти, ... Если у вас есть PK на имя + дата рождения + страна, весьма вероятно, что этот ключ не хороший кандидат. Он слишком велик для кластерного индекса. Уникальный идентификатор с использованием NEWSEQUENTIALID () обычно не считается узким ключом (16 байт), хотя он является последовательным.

Затем, когда вы выяснили, как уникально идентифицировать строки в вашей таблице, вы можете добавить PK. Если вы думаете, что не будете использовать его в своем запросе, не создавайте его кластеризованно. Вы все еще можете создать другой некластеризованный индекс, если вам когда-нибудь понадобится запросить его. Обратите внимание, что ПК автоматически создаст уникальный индекс.

Некластеризованные индексы всегда будут содержать кластеризованный ключ. Однако, если индексированные столбцы (+ ключевые столбцы) покрывают, не будет никакого ключевого поиска в кластеризованном индексе. Не забывайте, что вы можете также добавить «Включить» и «Где» в некластеризованный индекс. (использовать его мудро)

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

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

Это все теоретически, потому что мы не знаем вашу модель данных и используемые типы данных (A и B).

Жюльен Вавассер
источник
11

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

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

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

Будут ли запросы в вопросе быстрее работать без кластеризации?

Да, но с оговорками.

Поиск RID действительно более эффективен, чем поиск по ключу. Даже если все требуемые страницы находятся в памяти (весьма вероятно, для верхних уровней индекса), с навигацией по B-дереву кластерного индекса связаны затраты ЦП. Как следствие, SQL Server обычно может выполнять гораздо больше запросов RID, чем поиск ключей на единицу процессорного времени.

Предостережения

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

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

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

Мой собственный опыт показывает, что наличие уникальных кластеризованных индексов в столбцах идентификаторов очень полезно, и все вещи учитываются. Я обнаружил, что кучи проблематичны с точки зрения управления пространством, и я должен также упомянуть, что для работы некоторых функций SQL Server требуется уникальный кластеризованный индекс.

Пол Уайт говорит, что GoFundMonica
источник
8

На самом деле вам не нужно создавать кластерный индекс или первичный ключ, так как уникальные индексы и неуникальные индексы могут справиться с работой. SQL Server поддерживает кластеризованный индекс начиная с версии не ниже 1.1, но первичный ключ был просто «концепцией», которую программисты применяли, определяя уникальный индекс.

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

Давайте посмотрим на документацию по SQL Server, чтобы увидеть частичное описание некоторых параметров индексации, как показано ниже.

Кластерный индекс: https://msdn.microsoft.com/en-us/library/ms190457.aspx

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

Первичный ключ: https://msdn.microsoft.com/en-us/library/ms190457.aspx

  • Таблица может содержать только одно ограничение PRIMARY KEY.

  • Все столбцы, определенные в ограничении PRIMARY KEY, должны быть определены как NOT NULL.

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

Уникальный индекс: https://msdn.microsoft.com/en-us/library/ms187019.aspx

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

  • Вы можете указать UNIQUE Clustered Index, если кластерный индекс еще не существует для таблицы.

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

Когда я получу выгоду от того, что первичный ключ отделен от кластерного индекса?

Возможно, когда Кластерный Индекс Широкий (например, 5 столбцов текстовой информации, но Первичный Ключ маленький (INT или BIGINT), как вы, кажется, описываете.

  • Широкий кластеризованный индекс позволит вам быстро выбирать строки из индекса для подмножества запросов, которые предоставляют последовательные ответы из кластерного индекса (также известного как таблица ). Например, 5-колоночный кластерный индекс будет поддерживать сканирование столбцов C1, C2, C3, C4, C5 или C1, C2, C3, C4 и т. Д. Вплоть до C1.
  • Примечание. Если строки были большими, это может дать вам некоторые преимущества в скорости при выборе последовательного набора строк, особенно если другие столбцы в таблице регулярно включаются в набор результатов.
  • В этом случае вы можете использовать первичный ключ для ссылочной целостности, чтобы предоставить необходимое значение в качестве внешнего ключа для ограничения строк в других таблицах. PK является маленьким и, таким образом, FK является небольшим ударом по размеру ссылочной таблицы (таблиц).
  • Однако обратите внимание, что любой индекс, созданный в таблице с кластеризованным индексом, будет включать все столбцы кластера в другие индексы, которые вы создаете в этой таблице. Широкий кластеризованный индекс увеличит размер всех некластеризованных индексов в этой таблице.

Стоит ли делать первичный ключ одним кластерным индексом?

  • Если у вас есть маленький первичный ключ (INT или BIGINT), и это кластерный индекс, накладные расходы на столбцы кластера относительно невелики. Хотя Кластерный первичный ключ в этом случае также будет присутствовать в каждом индексе этой таблицы, это меньшая цена, чем широкий кластер, рассмотренный выше.

  • Этот кластеризованный индекс первичного ключа обычно не предлагает простой способ последовательного выбора множества строк.

  • Теперь, когда вы создали кластерный первичный ключ, как насчет тех других столбцов, которые вы когда-то планировали включить в кластерный индекс ?

  • Создайте уникальный (или неуникальный) индекс, необходимый для индексации широкого критерия поиска по столбцам C1, C2, C3, C4, C5. Значения в этом «Имитационном кластеризованном» индексе могут служить более быстрым путем поиска для этих 5 столбцов. Если существует неиндексированный столбец или два, которые также регулярно выбираются, они могут быть включены в индекс с помощью INCLUDE (Doctor_Name, Diagnosis_Synopsis).

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

Вам нужен кластерный индекс вообще?

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

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

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

Что теперь приводит вас к рассмотрению кучи:

Кучи и индексы: https://msdn.microsoft.com/en-us/library/hh213609.aspx

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

Но если у вас также есть «горячие точки» в большом наборе данных, вы также можете посмотреть на другой тип индекса:

Отфильтрованный индекс: https://msdn.microsoft.com/en-us/library/cc280372.aspx

  • Хорошо спроектированный отфильтрованный индекс повышает производительность запросов и качество плана выполнения, поскольку он меньше некластерного индекса полной таблицы и имеет отфильтрованную статистику. Отфильтрованная статистика является более точной, чем статистика полной таблицы, поскольку она охватывает только строки в отфильтрованном индексе .

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

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

http://use-the-index-luke.com/blog/2014-01/unreasonable-defaults-primary-key-clustering-key

Но в конечном итоге все возвращается к пониманию вашего приложения и разработке кода, таблиц, индексов и т. Д. В соответствии с выполняемой работой.

ДКП
источник
Что бы это ни стоило, в своей повседневной работе, если я нахожу таблицу, которая является кучей, я считаю ее, скорее всего, ошибкой, и уточняю у разработчиков, намеренно ли она была создана.
RLF
-2

Пара моментов для рассмотрения.

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

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

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

mustaccio
источник
Насколько высока должна быть ставка, чтобы это стало проблемой?
ypercubeᵀᴹ
@ypercube я могу сказать "это зависит"? Потому что это так. В отсутствие триггеров на столе я ожидаю, что начну испытывать некоторую конкуренцию с дюжиной потоков общим объемом 1K вставок в секунду.
Мустаччо
Я не согласен, но я спрашивал, как далеко можно зайти с одной горячей точкой. Я помню, как видел статью о вставке 30K строк в секунду в таблицу с IDENTITY в качестве CI (если память мне хорошо), но я не могу найти запись в блоге.
ypercubeᵀᴹ
Это обсуждение не имеет смысла в отсутствие конкретной рабочей нагрузки, работающей с конкретной схемой на конкретном оборудовании. Я надеюсь, что все мы можем согласиться с тем, что индекс монотонно возрастающей последовательности создаст «горячую точку»; создаст ли это неприемлемое узкое место, и нужно ли заботиться об этом или нет, зависит от обстоятельств.
Мустаччо