Ремус любезно указал, что максимальная длина VARCHAR
столбца влияет на предполагаемый размер строки и, следовательно, выделяет память, предоставляемую SQL Server.
Я попытался сделать немного больше исследований, чтобы раскрыть часть этого ответа. У меня нет полного или краткого объяснения, но вот что я нашел.
Репро сценарий
Я создал полный сценарий, который генерирует поддельный набор данных, для которого создание индекса занимает примерно 10-кратное время на моей машине для VARCHAR(256)
версии. Данные , используемые в точности то же самое, но первая таблица использует фактические максимальные длины 18
, 75
, 9
, 15
, 123
, и 5
, в то время как все столбцы использовать максимальную длину 256
во второй таблице.
Keying оригинальный стол
Здесь мы видим, что исходный запрос завершается примерно за 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
), и теперь время простоя заставляет Пола Рэндала просто сказать «ВАУ!».
Неудивительно, что сейчас тип ожидания №1 IO_COMPLETION
. Но почему генерируется так много ввода / вывода?
Фактический план запроса: VARCHAR (256)
Из плана запроса видно, что Sort
в VARCHAR(256)
версии запроса оператор имеет рекурсивный разлив (на 5 уровней глубиной!) . (В оригинальной версии разлива нет вообще.)
Ход выполнения запроса: 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, которые могут быть интересны. В нем немного говорится о разливе, перекосе потоков и распределении памяти для параллельных сортировок.