Ремус корисно вказав, що максимальна довжина VARCHAR
стовпця впливає на передбачуваний розмір рядка і тому надає пам'ять, яку надає SQL Server.
Я спробував зробити трохи більше досліджень, щоб розширити частину своєї відповіді на "від цього на каскад речей". У мене немає повного чи стислого пояснення, але ось що я знайшов.
Repro скрипт
Я створив повний сценарій, який генерує підроблений набір даних, на якому створення індексу займає приблизно 10 разів більше часу на моїй машині для VARCHAR(256)
версії. Дані , що використовуються в точності те ж саме, але перша таблиця використовує фактичні максимальні довжини 18
, 75
, 9
, 15
, 123
, і 5
, в той час як всі стовпці використовувати максимальну довжину 256
в другій таблиці.
Збереження оригінальної таблиці
Тут ми бачимо, що початковий запит завершується приблизно за 20 секунд, а логічні зчитування дорівнюють розміру таблиці ~1.5GB
(195 К сторінок, 8 К на сторінку).
-- CPU time = 37674 ms, elapsed time = 19206 ms.
-- Table 'testVarchar'. Scan count 9, logical reads 194490, physical reads 0
CREATE CLUSTERED INDEX IX_testVarchar
ON dbo.testVarchar (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO
Клавіша таблиці VARCHAR (256)
З VARCHAR(256)
таблиці ми бачимо, що минулий час різко збільшився.
Цікаво, що ні час процесора, ні логічні зчитування не збільшуються. Це має сенс, враховуючи, що таблиця має точно такі ж дані, але це не пояснює, чому минувший час настільки повільніше.
-- CPU time = 33212 ms, elapsed time = 263134 ms.
-- Table 'testVarchar256'. Scan count 9, logical reads 194491
CREATE CLUSTERED INDEX IX_testVarchar256
ON dbo.testVarchar256 (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO
Статистика вводу / виводу та очікування: оригінал
Якщо ми захопимо трохи детальніше (використовуючи p_perfMon, процедуру, яку я написав ), ми можемо побачити, що переважна більшість вводу-виводу виконується у LOG
файлі. Ми бачимо відносно скромну кількість вводу-виводу на фактичну ROWS
(основний файл даних), і основним типом очікування є LATCH_EX
, що вказує на вміст сторінки в пам'яті.
Ми також можемо побачити, що мій спінінг знаходиться десь між "поганим" і "шокуюче поганим", за словами Пола Рандала :)
Статистика вводу / виводу та очікування: VARCHAR (256)
Для VARCHAR(256)
версії статистика вводу / виводу та очікування виглядає зовсім інакше! Тут ми бачимо величезне збільшення вводу-виводу на файл даних ( ROWS
), а час перерви тепер змушує Пола Рандала просто сказати "WOW!".
Не дивно, що зараз номер 1 очікування IO_COMPLETION
. Але чому генерується стільки вводу-виводу?
Фактичний план запитів: VARCHAR (256)
З плану запитів ми бачимо, що Sort
оператор має рекурсивний розлив (глибиною 5 рівнів!) У VARCHAR(256)
версії запиту. (У початковій версії взагалі немає розливу.)
Хід запиту наживо: VARCHAR (256)
Ми можемо використовувати sys.dm_exec_query_profiles для перегляду ходу запитів у реальному часі в SQL 2014+ . У оригінальній версії цілі Table Scan
та Sort
обробляються без будь-яких розливів ( spill_page_count
залишається на 0
всьому протязі).
У VARCHAR(256)
версії, однак, ми бачимо, що розливи сторінок швидко накопичуються для Sort
оператора. Ось короткий знімок ходу запиту безпосередньо перед завершенням запиту. Дані тут агрегуються у всіх потоках.
Якщо я викопаю кожну нитку окремо, я бачу, що 2 потоки завершують сортування протягом приблизно 5 секунд (загалом @ 20 секунд, після 15 секунд, витрачених на сканування таблиці). Якби всі потоки прогресували з такою швидкістю, VARCHAR(256)
створення індексу завершилося б приблизно за той самий час, що і початкова таблиця.
Однак решта 6 ниток просуваються значно повільніше. Це може бути пов’язано із способом розподілу пам'яті та способом, яким нитки утримуються введенням-виведенням під час їх розсипання даних. Я точно не знаю, хоча.
Що ти можеш зробити?
Є кілька речей, які ви можете розглянути:
- Попрацюйте з постачальником, щоб повернутись до попередньої версії. Якщо це неможливо, дозвольте постачальнику, що ви не задоволені цією зміною, щоб вони могли розглянути можливість його повернення у майбутньому випуску.
- Додаючи свій індекс, подумайте про те,
OPTION (MAXDOP X)
де X
використовувати менший номер, ніж ваш поточний параметр на рівні сервера. Коли я використовував OPTION (MAXDOP 2)
цей специфічний набір даних на своїй машині, VARCHAR(256)
версія завершилася 25 seconds
(порівняно з 3-4 хвилинами з 8 нитками!). Можливо, що поведінка розливу посилюється вищим паралелізмом.
- Якщо додаткові інвестиції в обладнання - це можливість, профіліруйте введення / виведення (ймовірне вузьке місце) у вашій системі та обміркуйте використання SSD для зменшення затримки вводу / виводу, спричиненого розливом.
Подальше читання
У Пола Уайта є приємна публікація в блозі про внутрішні види SQL Server, які можуть зацікавити. Це трохи розповідає про розливання, перекручування потоку та розподіл пам'яті для паралельних сортів.