Основні міркування
Я бачу одну важливу перевагу для купи і одну для кластеризованих таблиць, а також третю увагу, яка може йти в будь-якому випадку.
Купа економить вам шар непрямості. Покажчики містять ідентифікатори рядків, що вказують безпосередньо (ну не дуже, але як можна прямо) на місце диска. Таким чином, пошук індексу щодо купи повинен коштувати приблизно вдвічі некластеризованого індексу на пошук кластерної таблиці.
Кластеризований індекс сортується сам по собі завдяки (майже) вільному індексу. Оскільки індекс кластеризації відображається у фізичному порядку даних, він займає відносно мало місця поверх самих фактичних даних, що, звичайно, вам доведеться зберігати. Оскільки це фізично впорядковано, сканування діапазону щодо цього індексу може шукати початкову точку, а потім дуже ефективно виконувати блискавку до кінцевої точки.
Індекси на купи опорних RID, які є 64 бітами. Як уже згадувалося, некластеризовані індекси в кластеризованій таблиці посилаються на кластеризаційний ключ, який може бути меншим (32-бітовим INT
), таким же (64-бітовим BIGINT
) або більшим (48-бітний DATETIME2()
плюс 32-розрядний INT
, або 128-бітний GUID). Очевидно, що більш широке посилання дає великі та дорожчі індекси.
Вимоги до місця
За допомогою цих двох таблиць:
CREATE TABLE TmpClustered
(
ID1 INT NOT NULL,
ID2 INT NOT NULL
)
ALTER TABLE TmpClustered ADD CONSTRAINT PK_Tmp1 PRIMARY KEY CLUSTERED (ID1)
CREATE UNIQUE INDEX UQ_Tmp1 ON TmpClustered (ID2)
CREATE TABLE TmpNonClustered
(
ID1 INT NOT NULL,
ID2 INT NOT NULL
)
ALTER TABLE TmpNonClustered ADD CONSTRAINT PK_Tmp2 PRIMARY KEY NONCLUSTERED (ID1)
CREATE UNIQUE INDEX UQ_Tmp2 ON TmpNonClustered (ID2)
... для кожного з 8,7 М записів необхідний простір становив 150 МБ для даних для обох; 120 МБ для індексів кластерної таблиці, 310 МБ для індексів некластеризованої таблиці. Це відображає, що кластерний індекс вужчий, ніж RID, і що індекс кластеризації є здебільшого "халявою". Без унікальних індексів ID2
необхідний простір індексів падає до 155 МБ для некластеризованої таблиці (наполовину, як можна було б очікувати), але всього 150 Кб для кластеризованої ПК - майже нічого.
Так некластеризований індекс 32-бітного поля в кластеризованій таблиці з 32-бітовим індексом (всього 64 біта, номінально) займав 120 МБ, тоді як індекс 32-бітного поля в купі з 64-бітним RID (всього 96 біт, номінально) займає 155 Мб, трохи менше 50% збільшення, можна було б наївно очікувати переходу від 64-бітних до 96-бітних клавіш, але, звичайно, є накладні витрати, що зменшує ефективну різницю в розмірах.
Складання двох таблиць та створення їх індексів займало однакову кількість часу для кожної таблиці. Проводячи прості тести, що включають сканування або пошук, я не виявив різниці між показниками продуктивності між таблицями, що відповідає білій книзі Microsoft, яка gbn корисно пов'язана. Згаданий папір показує суттєву різницю для високо одночасного доступу; Я не впевнений, чому це трапляється, сподіваємось, нам може сказати хтось із більшим досвідом, ніж я, з великими обсягами OLTP-систем.
Додавання ~ 40 байт даних випадкових змінної довжини суттєво не змінило цю еквівалентність. Заміна INT
s на широкі UUID також не відбулася (кожна таблиця була уповільнена приблизно в однаковій мірі). Ваш пробіг може відрізнятися, але в більшості випадків, чи є індекс, важливіше, ніж який вид.
Шматочки та шматочки
Проведення сканування діапазону проти некластеризованого індексу - або тому, що таблиця є купою, або індекс не є кластерним індексом - передбачає сканування індексу, а потім проведення пошуку проти таблиці для кожного звернення. Це може бути дуже дорого, тому іноді дешевше просто сканувати стіл. Однак ви можете обійти це за допомогою індексу покриття. Це стосується того, ви кластеризували свою таблицю чи ні.
Як зазначав @gbn, не існує простого способу ущільнення купи. Однак якщо ваша таблиця з часом поступово збільшується - дуже поширений випадок - тратиться буде мало, оскільки простір, звільнений видаленням, заповниться новими даними.
Я бачив, що декілька обговорень таблиць із купою та кластерами доводять цікавий аргумент, що купа без індексів поступається кластеризованій таблиці, оскільки вона завжди потребує сканування таблиці. Це, безумовно, вірно, але більш змістовним порівнянням є "велика добре індексована кластерна таблиця" проти "велика добре індексована купа". Якщо ваш стіл дуже маленький або ви завжди будете робити сканування столів, то це просто не має великого значення, кластеризуєте ви його чи ні.
Оскільки кожен індекс у кластерній таблиці посилається на індекс кластеризації, вони фактично всі охоплюють індекси. Запит, який посилається на індексований стовпець та стовпчик (кластери), може здійснити сканування індексу без будь-яких пошукових запитів у таблиці. Це, як правило, не цінно, якщо ваш індекс кластеризації є синтетичним ключем, але якщо це бізнес-ключ, який вам все одно потрібно буде отримати, це приємна особливість.
TL; DR
Я хлопець із зберігання даних, а не експерт OLTP. Для фактичних таблиць я майже завжди використовую індекс кластеризації на полі, який, швидше за все, потребує сканування діапазону, як правило, у полі дати. Для розмірних таблиць я кластеризую на ПК, тому він призначений для об'єднання об'єднань із таблицями фактів.
Існує кілька причин використовувати індекси кластеризації, але якщо жодна з цих причин не застосовується, то накладні витрати можуть бути не варті. Я підозрюю, що багато людей "ми завжди робили так" і "це просто найкраща практика" за людьми, що використовують кластерні індекси універсально. Спробуйте як зі своїми даними, так і зі своїм завантаженням, і подивіться, що найкраще працює.