Коли первинний ключ повинен бути оголошений некластеризованим?


169

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

Коли ви використовуєте NONCLUSTEREDпервинний ключ на відміну від CLUSTEREDпервинного ключа?

Спасибі заздалегідь

Відповіді:


187

Питання не в тому, «коли PK повинен бути NC», а натомість слід запитати «що є правильним ключем для кластерного індексу»?

І відповідь дійсно залежить від того, як ви запитуєте дані . Кластеризований індекс має перевагу перед усіма іншими індексами: оскільки він завжди включає всі стовпці, завжди охоплює. Тому запити, які можуть використовувати кластерний індекс, звичайно, не потребують пошуку для задоволення деяких проектованих стовпців та / або предикатів.

Ще один фрагмент головоломки - як можна використовувати індекс ? Існує три типові схеми:

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

Отже, якщо ви проаналізуєте очікуване навантаження (запити) і виявите, що велика кількість запитів використовує певний індекс, оскільки вони використовують певну схему доступу, яка отримує користь від індексу, має сенс запропонувати цей індекс як кластерний індекс.

Ще один фактор полягає в тому, що кластерний індексний ключ - це ключ пошуку, який використовується всіма некластеризованими індексами, і тому широкий кластерний індексний ключ створює ефект пульсації та розширює всі некластеризовані індекси, а широкі індекси означають більше сторінок, більше вводу / виводу , більше пам’яті, менше добра.

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

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

Отже, тепер, коли ми знаємо, що таке хороший кластерний індексний ключ, чи відповідає первинний ключ (який є логічною властивістю моделювання даних) вимогам? Якщо так, то ПК слід кластеризувати. Якщо ні, то ПК має бути некластеризованим.

Для прикладу розглянемо таблицю фактів продажів. Кожен запис має ідентифікатор, який є первинним ключем. Але переважна більшість запитів запитує дані між датою та іншою датою, тому найкращим кластеризованим індексним ключем буде дата продажу , а не ідентифікатор . Інший приклад наявності індексованого кластерного індексу від первинного ключа - це дуже низький селективний ключ, наприклад, "категорія" або "стан", ключ із лише дуже маленькими чіткими значеннями. Якщо кластерний індексний ключ із цією клавішею низької селективності є крайньою лівою клавішею, наприклад (state, id), часто має сенс через сканування діапазонів, які шукають усі записи у певному "стані".

Останнє зауваження про можливість некластеризованого первинного ключа над групою (тобто кластерного індексу взагалі немає). Це може бути вірним сценарієм, типовою причиною є те, коли продуктивність об'ємної вставки є критичною, оскільки купи мають значно кращу пропускну здатність масової вставки порівняно з кластерними індексами.


1
Що означає "замовлення за вимогами, коли індекс може задовольняти замовлення, не вимагаючи сортування" зупинка і перехід "?
Майк Шеррілл 'Відкликання котів'

2
@RemusRusanu. +1 Дуже корисна відповідь. Одне запитання щодо прикладу (state, id). У цьому прикладі вимога "хороший кластерний індекс зростає, щоб не випадково" вимога не буде виконана, чи не так? Тож чи можемо ми вважати це хорошим кластерним індексом?
Ліхо

26

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

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

Скажіть, що у мене є таблиця людей, а у цих людей стовпець "Країна" та унікальний первинний ключ. Це демографічна таблиця, тож це єдині речі, які мене хвилюють; яка країна і скільки унікальних людей пов'язані з цією країною.

Таким чином, я лише колись вірогідний ВИБІР ТУТ або ЗАМОВИТИ стовпцем "Країна"; кластерний індекс на первинному ключі не приносить мені користі, я не отримую доступу до цих даних ПК, я отримую доступ до цього іншого стовпця. Оскільки в таблиці я можу мати лише один кластерний індекс, оголошення мого ПК як кластеризованого не дозволить мені використовувати кластерний індекс для країни.

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

Якщо ви кладете кластерний індекс у стовпчик ІДЕНТИЧНОСТІ, то всі ваші вставки відбуватимуться на останній сторінці таблиці - і ця сторінка заблокована протягом тривалості кожної ідентичності. Нічого страшного ... якщо у вас 5000 людей, які хочуть останньої сторінки. Тоді у вас є велика суперечка щодо цієї сторінки

Зауважте, що це не так у наступних версіях.


3
FIY, ви згадали про SQL Server 6.5: dba.stackexchange.com/questions/1584/…
gbn

15

Якщо ваш основний ключ є UNIQUEIDENTIFIER, обов'язково вкажіть, що це NONCLUSTERED. Якщо ви зробите це кластеризовано, для кожної вставки потрібно буде зробити перетасування записів, щоб вставити новий рядок у правильне положення. Це призведе до працездатності танка.


1
Хоча я намагаюся уникати UUID для кластеризованих ключів, я вважаю, що міркування вище можуть бути неповними. SQL-сервер не обов'язково переставляє рядки, щоб вставити правильне положення (якщо ви маєте на увазі "між нижчим і вищим значенням"). Розгляньте вставку в середину таблиці трильйона рядків. Додаткова непрямість - це потреба, яка може бути саме тим, що ви мали на увазі. Послідовний UNIQUEIDENTIFIERтип також існує і має однакову ймовірність створення унікальних ключів, хоча він все ще має 128-ти розмір.
Чарльз Бернс

7

Дуже поширений приклад:

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

Звичайно, "це залежить" - як майже завжди - правильна відповідь, але більшість програм (не BI-Reports) працюватимуть клієнтами (наприклад, ви ввійдете як клієнт 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.

BTW: правильно, що Customerтаблиця є кластеризованою PK (тому CustomerID, що це "таблиця верхнього рівня", і в типовому додатку - в основному буде запитуватися його CustomerID.

Чисті таблиці пошуку , як , наприклад , Gendersабо , InvoiceTypesабо PaymentTypeще один приклад таблиць , які повинні бути згруповані по його ПК (бо ви будете зазвичай приєднатися до них GenderId, InvoiceTypeIdабо PaymentTypeId).


2

Коли кластерний індекс вважається більш корисним для всієї системи, ніж кластерний ПК, використовуючи певний показник продуктивності. На столі може бути лише один кластерний індекс.

Приклад показників ефективності - це час однократного запиту (швидкість), інтеграція загального часу запиту проти таблиці (ефективність) та необхідність додавання багатьох включених стовпців до дуже великого некластеризованого індексу для досягнення ефективності, схожої на кластеризовану (розмір ).

Це може статися, коли дані, як правило, витягуються за допомогою індексу, який не є унікальним, містить нулі (не дозволено в ПК), або ПК додано з другої причини (наприклад, тиражування чи ідентифікація запису траси аудиту).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.