2008-11-10 3 views
29

Я работаю над базой данных, которая обычно использует GUID как первичные ключи.Должен ли я избавиться от кластеризованных индексов на столбцах Guid

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

Как вы думаете, следует ли избавиться от всех кластеризованных индексов и заменить их некластеризованными индексами?

Почему SQL-тюнер производительности не предлагает эту рекомендацию?

+0

Взгляните на следующее сообщение Пола Рэндала. [Кластеризованный или некластеризованный индекс по случайному GUID?] (Http://www.sqlskills.com/blogs/paul/clustered-or-nonclustered-index-on-a-random-guid/) – Lijo 2016-09-09 20:55:57

ответ

25

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

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

Так что да, не класть указатель на GUID.

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

+2

С SQL 2005 и newsequentialid() проблема фрагментации уходит в значительной степени. Лучше всего измерить, посмотрев на sys.dm_db_index_physical_stats и sys_indexes. – RoadWarrior 2008-11-10 17:12:04

+0

Вы все равно не получаете никаких преимуществ в своих запросах. Вы должны класть только «UNIQUEIDENTIFIER», если вам нужно, например. для тиражирования. – 2016-06-27 22:29:52

4

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

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

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

0

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

0

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

+0

Кластеризация не влияет на поиск скорость - уникальный некластеризованный индекс должен выполнять эту работу. – 2008-11-10 11:18:59

4

Если вы используете NewId(), вы можете переключиться на NewSequentialId(). Это должно помочь вставить perf.

5

В то время как кластеризация на GUID обычно является плохой идеей, имейте в виду, что GUID могут при определенных обстоятельствах cause fragmentation even in non-clustered indexes.

Обратите внимание: если вы используете SQL Server 2005, то функция newsequentialid() производит последовательный GUID. Это помогает предотвратить проблему фрагментации.

Я предлагаю использовать SQL запрос вроде следующего для измерения фрагментации, прежде чем принимать какие-либо решения (извините не-ANSI синтаксис):

SELECT OBJECT_NAME (ips.[object_id]) AS 'Object Name', 
     si.name AS 'Index Name', 
     ROUND (ips.avg_fragmentation_in_percent, 2) AS 'Fragmentation', 
     ips.page_count AS 'Pages', 
     ROUND (ips.avg_page_space_used_in_percent, 2) AS 'Page Density' 
FROM sys.dm_db_index_physical_stats 
    (DB_ID ('MyDatabase'), NULL, NULL, NULL, 'DETAILED') ips 
CROSS APPLY sys.indexes si 
WHERE si.object_id = ips.object_id 
AND si.index_id = ips.index_id 
AND ips.index_level = 0; 
2

Да, нет никакого смысла в том, кластерный индекс на случайной величины ,

Возможно, вам нужны кластерные индексы SOMEWHERE в вашей базе данных. Например, если у вас есть таблица «Автор» и таблица «Книга» с внешним ключом «Автор», и если у вас есть запрос в приложении, который говорит «выберите ... из книги, где AuthorId = .. », тогда вы будете читать набор книг. Это будет быстрее, если эти книги будут физически рядом друг с другом на диске, так что голова диска не должна отскакивать от сектора к сектору, собирающего все книги этого автора.

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

Внесите изменения.

А потом тест, потому что вы никогда не знаете ...

24

Вы почти наверняка хотите создать кластерный индекс каждой таблицы в базе данных. Если таблица не имеет кластерного индекса, это то, что называется «кучей», а производительность большинства типов общих запросов - less for a heap than for a clustered index table.

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

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

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

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

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

Для некластеризованного индекса он должен пересекать индекс один раз для каждой строки, которую он извлекает ...

ПРИМЕЧАНИЕ: EDIT
Для решения последовательного вопроса для Guid ключевых столбцов, следует помнить, что SQL2k5 имеет NEWSEQUENTIALID(), что делает фактически генерировать Guids «старый» последовательный путь.

или вы можете исследовать Джимми Nielsens расческу Guid algotithm, который реализуется в стороне клиента код:

COMB Guids

0

Как большинство уже упоминали , избегайте использования случайного идентификатора в кластерном индексе - вы не получите преимуществ кластеризации. Фактически, у вас будет повышенная задержка. Избавиться от них - солидный совет. Также имейте в виду, что newsequentialid() может быть чрезвычайно проблематичным в сценарии репликации с несколькими мастерами. Если базы данных A и B одновременно вызывают newsequentialid() перед репликацией, у вас возникнет конфликт.

Смежные вопросы