Я не могу точно сказать, почему это происходит, но я считаю, что я разработал хорошую модель поведения с помощью тестирования методом грубой силы. Следующие выводы применимы только при загрузке данных в один столбец и с очень хорошо распределенными целыми числами.
Сначала я попытался изменить количество строк, вставленных в CCI, используя TOP
. Я использовал ID % 16000
для всех тестов. Ниже приведен график сравнения строк, вставленных в сжатый размер сегмента группы строк:
Ниже приведен график строк, вставленных в процессорное время в мс. Обратите внимание, что ось X имеет другую начальную точку:
Мы можем видеть, что размер сегмента группы строк растет с линейной скоростью и использует небольшое количество процессора до примерно 1 млн. Строк. В этот момент размер группы строк резко уменьшается, а загрузка ЦП резко возрастает. Казалось бы, мы платим высокую цену в CPU за это сжатие.
При вставке менее 1024000 строк я получил в CCI открытую группу строк. Однако принудительное сжатие с использованием REORGANIZE
или REBUILD
не оказало влияния на размер. Кроме того, мне показалось интересным, что, когда я использовал переменную для, TOP
я получал открытую группу строк, но RECOMPILE
я получал закрытую группу строк.
Затем я проверил, изменяя значение модуля, сохраняя количество строк одинаковым. Вот пример данных при вставке 102400 строк:
╔═══════════╦═════════╦═══════════════╦═════════════╗
║ TOP_VALUE ║ MOD_NUM ║ SIZE_IN_BYTES ║ CPU_TIME_MS ║
╠═══════════╬═════════╬═══════════════╬═════════════╣
║ 102400 ║ 1580 ║ 13504 ║ 352 ║
║ 102400 ║ 1590 ║ 13584 ║ 316 ║
║ 102400 ║ 1600 ║ 13664 ║ 317 ║
║ 102400 ║ 1601 ║ 19624 ║ 270 ║
║ 102400 ║ 1602 ║ 25568 ║ 283 ║
║ 102400 ║ 1603 ║ 31520 ║ 286 ║
║ 102400 ║ 1604 ║ 37464 ║ 288 ║
║ 102400 ║ 1605 ║ 43408 ║ 273 ║
║ 102400 ║ 1606 ║ 49360 ║ 269 ║
║ 102400 ║ 1607 ║ 55304 ║ 265 ║
║ 102400 ║ 1608 ║ 61256 ║ 262 ║
║ 102400 ║ 1609 ║ 67200 ║ 255 ║
║ 102400 ║ 1610 ║ 73144 ║ 265 ║
║ 102400 ║ 1620 ║ 132616 ║ 132 ║
║ 102400 ║ 1621 ║ 138568 ║ 100 ║
║ 102400 ║ 1622 ║ 144512 ║ 91 ║
║ 102400 ║ 1623 ║ 150464 ║ 75 ║
║ 102400 ║ 1624 ║ 156408 ║ 60 ║
║ 102400 ║ 1625 ║ 162352 ║ 47 ║
║ 102400 ║ 1626 ║ 164712 ║ 41 ║
╚═══════════╩═════════╩═══════════════╩═════════════╝
Вплоть до значения мода 1600 размер сегмента группы строк линейно увеличивается на 80 байтов для каждых дополнительных 10 уникальных значений. Это интересное совпадение, что BIGINT
традиционно занимает 8 байт, а размер сегмента увеличивается на 8 байт для каждого дополнительного уникального значения. После значения мода 1600 размер сегмента быстро увеличивается, пока не стабилизируется.
Также полезно смотреть на данные, оставляя значение модуля одинаковым и изменяя количество вставленных строк:
╔═══════════╦═════════╦═══════════════╦═════════════╗
║ TOP_VALUE ║ MOD_NUM ║ SIZE_IN_BYTES ║ CPU_TIME_MS ║
╠═══════════╬═════════╬═══════════════╬═════════════╣
║ 300000 ║ 5000 ║ 600656 ║ 131 ║
║ 305000 ║ 5000 ║ 610664 ║ 124 ║
║ 310000 ║ 5000 ║ 620672 ║ 127 ║
║ 315000 ║ 5000 ║ 630680 ║ 132 ║
║ 320000 ║ 5000 ║ 40688 ║ 2344 ║
║ 325000 ║ 5000 ║ 40696 ║ 2577 ║
║ 330000 ║ 5000 ║ 40704 ║ 2589 ║
║ 335000 ║ 5000 ║ 40712 ║ 2673 ║
║ 340000 ║ 5000 ║ 40728 ║ 2715 ║
║ 345000 ║ 5000 ║ 40736 ║ 2744 ║
║ 350000 ║ 5000 ║ 40744 ║ 2157 ║
╚═══════════╩═════════╩═══════════════╩═════════════╝
Похоже, когда количество вставленных строк <~ 64 *, число уникальных значений мы видим относительно слабое сжатие (2 байта на строку для мода <= 65000) и низкое, линейное использование ЦП. Когда вставленное количество строк> ~ 64 *, число уникальных значений мы видим гораздо лучше, сжатие и более высокое, по-прежнему линейное использование ЦП. Между двумя состояниями есть переход, который мне нелегко моделировать, но это видно на графике. Не похоже, что мы видим максимальную загрузку ЦП при вставке ровно 64 строк для каждого уникального значения. Скорее, мы можем вставить максимум 1048576 строк в группу строк, и мы увидим гораздо более высокую загрузку ЦП и сжатие, если на уникальное значение будет приходиться более 64 строк.
Ниже приведен контурный график того, как время процессора изменяется в зависимости от количества вставленных строк и количества уникальных строк. Мы можем видеть шаблоны, описанные выше:
Ниже приведен контурный график пространства, используемого сегментом. После определенного момента мы начинаем видеть намного лучшее сжатие, как описано выше:
Кажется, здесь работают как минимум два разных алгоритма сжатия. Учитывая вышесказанное, имеет смысл, что мы увидим максимальное использование процессора при вставке 1048576 строк. Также имеет смысл, что мы видим наибольшее использование ЦП в этот момент при вставке около 16000 строк. 1048576/64 = 16384.
Я загрузил все мои необработанные данные здесь в случае , если кто - то хочет , чтобы проанализировать его.
Стоит упомянуть, что происходит с параллельными планами. Я наблюдал это поведение только с равномерно распределенными значениями. При параллельной вставке часто присутствует элемент случайности, а потоки обычно не сбалансированы.
Поместите 2097152 строк в промежуточную таблицу:
DROP TABLE IF EXISTS STG_2097152;
CREATE TABLE dbo.STG_2097152 (ID BIGINT NOT NULL);
INSERT INTO dbo.STG_2097152 WITH (TABLOCK)
SELECT TOP (2097152) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Эта вставка заканчивается менее чем за секунду и имеет плохое сжатие:
DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);
INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT ID % 16000
FROM dbo.STG_2097152
OPTION (MAXDOP 2);
Мы можем видеть эффект несбалансированных потоков:
╔════════════╦════════════╦══════════════╦═══════════════╗
║ state_desc ║ total_rows ║ deleted_rows ║ size_in_bytes ║
╠════════════╬════════════╬══════════════╬═══════════════╣
║ OPEN ║ 13540 ║ 0 ║ 311296 ║
║ COMPRESSED ║ 1048576 ║ 0 ║ 2095872 ║
║ COMPRESSED ║ 1035036 ║ 0 ║ 2070784 ║
╚════════════╩════════════╩══════════════╩═══════════════╝
Существуют различные приемы, которые мы можем сделать, чтобы заставить потоки быть сбалансированными и иметь одинаковое распределение строк. Вот один из них:
DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);
INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT FLOOR(0.5 * ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) % 15999
FROM dbo.STG_2097152
OPTION (MAXDOP 2)
Здесь важно выбрать нечетное число для модуля. SQL Server последовательно сканирует промежуточную таблицу, вычисляет номер строки, а затем использует циклическое распределение для размещения строк в параллельных потоках. Это означает, что мы получим идеально сбалансированные потоки.
Вставка занимает около 40 секунд, что похоже на последовательную вставку. Мы получаем красиво сжатые группы строк:
╔════════════╦════════════╦══════════════╦═══════════════╗
║ state_desc ║ total_rows ║ deleted_rows ║ size_in_bytes ║
╠════════════╬════════════╬══════════════╬═══════════════╣
║ COMPRESSED ║ 1048576 ║ 0 ║ 128568 ║
║ COMPRESSED ║ 1048576 ║ 0 ║ 128568 ║
╚════════════╩════════════╩══════════════╩═══════════════╝
Мы можем получить те же результаты, вставив данные из исходной промежуточной таблицы:
DROP TABLE IF EXISTS dbo.CCI_BIGINT;
CREATE TABLE dbo.CCI_BIGINT (ID BIGINT NOT NULL, INDEX CCI CLUSTERED COLUMNSTORE);
INSERT INTO dbo.CCI_BIGINT WITH (TABLOCK)
SELECT t.ID % 16000 ID
FROM (
SELECT TOP (2) ID
FROM (SELECT 1 ID UNION ALL SELECT 2 ) r
) s
CROSS JOIN dbo.STG_1048576 t
OPTION (MAXDOP 2, NO_PERFORMANCE_SPOOL);
Здесь циклическое распределение используется для производной таблицы, s
поэтому одно сканирование таблицы выполняется в каждом параллельном потоке:
В заключение, при вставке равномерно распределенных целых чисел вы можете увидеть очень высокое сжатие, когда каждое уникальное целое число встречается более 64 раз. Это может быть связано с использованием другого алгоритма сжатия. ЦП может быть дорогостоящим для достижения этого сжатия. Небольшие изменения в данных могут привести к существенным различиям в размере сжатого сегмента группы строк. Я подозреваю, что наихудший случай (с точки зрения процессора) будет редкостью в дикой природе, по крайней мере, для этого набора данных. Это еще сложнее увидеть при выполнении параллельных вставок.