Должен ли составной первичный ключ быть кластеризованным в SQL Server? - PullRequest
10 голосов
/ 23 декабря 2008

Рассмотрим пример таблицы (при условии, что SQL Server 2005):

create table product_bill_of_materials
(
    parent_product_id int not null,
    child_product_id int not null,
    quantity int not null
)

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

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

Ответы [ 5 ]

11 голосов
/ 23 декабря 2008

Как уже было сказано несколькими другими, это зависит от того, как вы получите доступ к столу. Имейте в виду, однако, что любая СУБД должна иметь возможность использовать кластерный индекс для поиска по одному столбцу, пока этот столбец появляется первым. Например, если ваш кластерный индекс включен (parent_id, child_id), вам не нужен другой отдельный индекс для (parent_id).

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

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

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

В вашем случае я бы, вероятно, просто включил кластерный PK (parent_id, child_id) для начала, а затем добавил бы некластеризованный индекс, только если я увидел проблему с производительностью, которая помогла бы ему.

6 голосов
/ 23 декабря 2008

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

Лучший пример - история продаж для клиента.

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

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

РЕДАКТИРОВАТЬ: Давайте возьмем эту конкретную таблицу для обсуждения - она ​​покажет еще больше тонкостей.

«Естественный» первичный ключ - это, по всей вероятности, парентид + childid. Но в какой последовательности? Парентид + childid не более уникален, чем childid + парентид. Для целей кластеризации, какой порядок больше подходит? Можно было бы предположить, что это должен быть parentid + childid, так как мы захотим спросить: «Для данного элемента, каковы его составляющие»? Но не маловероятно ли, что вы захотите пойти другим путем и спросить: «Для данного компонента, из каких элементов он является компонентом?».

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

Но это по-прежнему не диктует, что должно быть сгруппировано; что в конечном итоге, вероятно, будет определяться тем, какие запросы на самом деле должны будут получить запись для поля «Количество».

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

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

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

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

2 голосов
/ 23 декабря 2008

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

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

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

Что касается того, что кластеризовать, вы должны подумать о том, как лучше всего использовать таблицу. Если ваша таблица подвергается большому количеству запросов диапазона (ГДЕ col1 находится между a и b), тогда кластеризуйте таблицу так, чтобы запросы диапазона уже были установлены в порядке на диске. В SQL Server иногда мы получаем кластер бесплатно с PK и забываем о том, что лучше кластеризовать для начала.

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

Отличный вопрос.

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

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

Я согласен с ответом Митчела: кластер работает так, как вы чаще всего будете запрашивать.

...