Чи має значення порядок стовпців в індексі ПК?


33

У мене є кілька дуже великих таблиць з однаковою базовою структурою. Кожен має колонку RowNumber (bigint)та DataDate (date)стовпчик. Дані завантажуються за допомогою SQLBulkImport щовечора, і жодні "нові" дані ніколи не завантажуються - його історичний запис (стандарт SQL, а не Enterprise, тому ніякого розділу).

Оскільки кожен біт даних потрібно пов'язати з іншими системами, і кожна RowNumber/DataDateкомбінація унікальна, це мій первинний ключ.

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

Я також помічаю, що моя фрагментація завжди ДУЖЕ високо ~ 99%.

Тепер, оскільки кожен DataDateз’являється лише один раз, я б очікував, що індексатор просто додаватиме на сторінки щодня, але мені цікаво, чи це насправді індексація на основі RowNumberпершого, а значить, потрібно перенести все інше?


Rownumberце не стовпець ідентичності, це int, породжений зовнішньою системою (на жаль). Він скидається на початку кожного DataDate.

Приклад даних

RowNumber | DataDate | a | b | c..... 
   1      |2013-08-01| x | y | z 
   2      |2013-08-01| x | y | z 
...
   1      |2013-08-02| x | y | z 
   2      |2013-08-02| x | y | z 
...

Дані завантажуються по RowNumberпорядку, один DataDateна навантаження.

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

Відповіді:


50

Чи має значення порядок стовпців в індексі ПК?

Так.

За замовчуванням обмеження первинного ключа застосовується в SQL Server унікальним кластерним індексом. Кластерний індекс визначає логічний порядок рядків у таблиці. Може бути додано кілька додаткових сторінок індексу для представлення верхніх рівнів індексу b-дерева, але найнижчий (листовий) рівень кластерного індексу - це просто логічний порядок самих даних.

Щоб зрозуміти це, рядки на сторінці не обов'язково фізично зберігаються в кластерному порядку індексних ключів. Всередині сторінки є окрема структура непрямості, яка зберігає вказівник на кожен рядок. Ця структура сортується за кластеризованими індексними ключами. Також кожна сторінка має вказівник на попередню та наступну сторінку на одному рівні в кластерному порядку індексних ключів.

За допомогою кластеризованого первинного ключа (RowNumber, DataDate)рядки логічно впорядковуються спочатку, RowNumberа потім по DataDate- тому всі рядки, де RowNumber = 1логічно згруповані, потім рядки, де RowNumber = 2і так далі.

Коли ви додаєте нові дані (з RowNumbers1 по n), нові рядки логічно належать до існуючих сторінок, тому SQL Server, ймовірно, повинен зробити багато роботи з розділенням сторінок, щоб звільнити місце. Вся ця діяльність породжує багато зайвої роботи (включаючи реєстрацію змін) без вигоди.

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

Зміна кластерного індексу на (DataDate, RowNumber) означає, що нові дані (з, мабуть, вищими, DataDatesніж зараз зберігаються) додаються до логічного кінця кластерного індексу на нових сторінках. Це призведе до видалення зайвих накладних витрат на розбиття сторінок і призведе до більш швидкого завантаження. Менш фрагментовані дані також означають, що активність читання наперед (читання сторінок з диска, перш ніж вони знадобляться для запиту, що триває) може бути ефективнішою.

Якщо нічого іншого, ваші запити будуть набагато частіше шукати, DataDateніж RowNumber. Кластеризований індекс на (DataDate, RowNumber) підтримує пошук індексу DataDate(і потім RowNumber). Існуюча композиція підтримує лише пошук RowNumber(і лише тоді, можливо, на DataDate). Можливо, ви зможете скинути існуючий некластеризований індекс DataDateпісля того, як буде змінено первинний ключ. Кластерний індекс буде ширшим за некластеризований індекс, який він замінює, тому слід перевірити, щоб продуктивність залишалася прийнятною.

Імпортуючи нові дані за допомогою bcp, ви можете отримати більш високу ефективність, якщо дані у файлі імпорту відсортовані за кластеризованими індексними клавішами (в ідеалі (DataDate, RowNumber) і ви вкажете bcpпараметр:

-h "ORDER(DataDate,RowNumber), TABLOCK"

Для найкращої продуктивності завантаження даних, ви можете спробувати домогтися мінімально зафіксованих даних. Для отримання додаткової інформації див:


4
Відмінна відповідь - я тепер знаю, що мені робити І чому. Я так думав, але не ЗНАНЬ так! Дякую.
BlueChippy

Взяв LOOOOONG, аби передати БД в мій локальний SQL Server для тестування: Перш ніж змінити завантаження індексу зайняло 45 хвилин ... після, це знадобилося всього 5 !!!
BlueChippy

13

Так, порядок є критичним. Я дуже сумніваюся, що ви коли-небудь запитуєте RowNumber (наприклад, WHERE RowNumber=1). Помірний часовий ряд запитується за датою ( WHERE DataDate BEWEEN @start AND @end), і такі запити потребують кластерної організації від DataDate.

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


У мене також є некластеризовані індекси на DataDate, які, як ви кажете, часто ставлять WHEREу запитах.
BlueChippy

1
Якщо ЗАМОВЛЕННЯ стовпців є критичним, чи вплине на порядок включення бачення мого вводу-виводу? Думаю, що це замовлення від RowNumber і тому доводиться щоразу робити багато роботи над індексами, тоді як він повинен базуватися на DataDate?
BlueChippy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.