이 동작이 왜 발생하는지 정확하게 말할 수는 없지만 무차별 강제 테스트를 통해 올바른 동작 모델을 개발했다고 생각합니다. 다음 결론은 단일 열에 데이터를로드 할 때와 잘 분산 된 정수로만 적용됩니다.
먼저을 사용하여 CCI에 삽입되는 행 수를 변경하려고했습니다 TOP
. ID % 16000
모든 테스트에 사용 했습니다. 아래는 압축 된 행 그룹 세그먼트 크기에 삽입 된 행을 비교 한 그래프입니다.
아래는 ms 단위로 CPU 시간에 삽입 된 행의 그래프입니다. X 축의 시작점이 다릅니다.
행 그룹 세그먼트 크기가 선형 속도로 증가하고 약 1M 행까지 소량의 CPU를 사용함을 알 수 있습니다. 이 시점에서 행 그룹 크기가 크게 줄어들고 CPU 사용량이 크게 증가합니다. 해당 압축에 대해 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의 mod 값이 될 때까지 행 그룹 세그먼트 크기는 추가 10 개의 고유 한 값마다 80 바이트 씩 선형으로 증가합니다. BIGINT
전통적으로 8 바이트를 차지하고 각 추가 고유 값에 대해 세그먼트 크기가 8 바이트 씩 증가 한다는 것은 우연의 일치입니다 . 1600의 mod 값을 지나면 세그먼트 크기는 안정화 될 때까지 빠르게 증가합니다.
모듈러스 값을 동일하게두고 삽입 된 행 수를 변경할 때 데이터를 보는 것도 도움이됩니다.
╔═══════════╦═════════╦═══════════════╦═════════════╗
║ 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 * 고유 값의 수는 상대적으로 압축률이 낮고 (mod <= 65000의 경우 행 당 2 바이트) 선형 CPU 사용량이 적습니다. 삽입 된 행 수가> 64 * 고유 값의 수인 경우 훨씬 더 나은 압축과 선형 CPU 사용을 볼 수 있습니다. 모델링하기 쉽지 않은 두 상태 사이에 전환이 있지만 그래프에서 볼 수 있습니다. 각각의 고유 한 값에 대해 정확히 64 개의 행을 삽입 할 때 최대 CPU 사용량을 볼 수는 없습니다. 오히려 최대 1048576 개의 행만 행 그룹에 삽입 할 수 있으며 고유 한 값당 64 개가 넘는 행이 있으면 CPU 사용률과 압축률이 훨씬 높아집니다.
아래는 삽입 된 행 수와 고유 한 행 수가 변경 될 때 CPU 시간이 어떻게 변하는 지에 대한 등고선도입니다. 위에서 설명한 패턴을 볼 수 있습니다.
아래는 세그먼트가 사용하는 공간의 등고선도입니다. 특정 시점이 지나면 위에서 설명한 것처럼 훨씬 더 나은 압축이 시작됩니다.
여기에는 적어도 두 개의 다른 압축 알고리즘이 작동하는 것 같습니다. 위의 내용을 감안할 때 1048576 행을 삽입 할 때 최대 CPU 사용량을 볼 수 있습니다. 약 16000 개의 행을 삽입 할 때 해당 시점에서 가장 많은 CPU 사용량을 볼 수 있습니다. 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;
이 인서트는 1 초 이내에 마무리되며 압축률이 낮습니다.
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 번 이상 나타날 때 매우 높은 압축률을 볼 수 있습니다. 다른 압축 알고리즘이 사용 되었기 때문일 수 있습니다. 이 압축을 달성하려면 CPU 비용이 많이들 수 있습니다. 데이터가 조금만 변경되면 압축 된 행 그룹 세그먼트의 크기가 크게 달라질 수 있습니다. 적어도이 데이터 세트에서 최악의 경우 (CPU 관점에서)를 보는 것이 드문 일이라고 생각합니다. 병렬 인서트를 수행 할 때는보기가 더 어렵습니다.