Почему создание индекса занимает больше времени после увеличения размера столбца?

18

Наш поставщик изменил ширину столбцов практически для каждого столбца во всей базе данных. База данных составляет около 7 ТБ, более 9000 таблиц. Мы пытаемся создать индекс для таблицы с 5,5 миллиардами строк. До обновления вендора мы могли создать индекс за 2 часа. Теперь это занимает дни. Что они сделали, так это увеличили любой размер varchar (xx) до varchar (256). Таким образом, большинство столбцов раньше были varchar (18) или varchar (75) и т. Д.

Во всяком случае, первичный ключ состоит из 6 столбцов, чтобы общая ширина составляла 126 символов. Теперь после обновления первичный ключ составляет 1283 символа, что нарушает ограничение SQL Server в 900 символов. Вся ширина столбца таблицы изменилась от общего количества объединенных varchar 1049 до общего числа объединенных varchar 4009.

Увеличение данных не происходит, таблица не занимает больше «места», чем до увеличения ширины столбца, но производительность для создания чего-то простого, например индекса, теперь занимает неоправданное количество времени.

Может кто-нибудь объяснить, почему создание и индексация занимает так много времени, когда единственное, что нужно сделать, - это увеличить размер столбцов?

Индекс, который мы пытаемся создать, не является кластеризованным, поскольку pk является кластеризованным индексом. После нескольких попыток создать индекс мы сдались. Я думаю, что это бежало 4 или 5 дней без завершения.

Я попробовал это в непроизводственной среде, сделав снимок файловой системы и подняв базу данных на более тихом сервере.

Даниэль Миллер
источник

Ответы:

12

Ремус любезно указал, что максимальная длина 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, которые могут быть интересны. В нем немного говорится о разливе, перекосе потоков и распределении памяти для параллельных сортировок.

Джефф Паттерсон
источник
11

Промежуточная таблица сортировки будет по-разному оценена между двумя случаями. Это приведет к различным запросам на предоставление памяти ( VARCHAR(256)будет больше) и, вероятно, к гораздо меньшему фактическому предоставлению, в процентном отношении, по сравнению с «идеальным» запросом. Я предполагаю, что это приводит к разливам во время сортировки.

Тестируя скрипт от Джеффа (только на 100 тыс. Строк), я отчетливо вижу разницу в оценочном размере строки (141B против 789B). С этого на вещи каскадом.

Ремус Русану
источник
8
Я уверен, что Пол докажет более тщательный и полный ответ, возможно, включая стеки вызовов, которые будут использованы в качестве учебного материала командой разработчиков. Снова ...
Ремус Русану