この動作が発生している理由を正確に言うことはできませんが、ブルートフォーステストによって動作の優れたモデルを開発したと思います。次の結論は、データが単一の列にロードされ、整数が非常によく分散されている場合にのみ適用されます。
最初に、を使用してCCIに挿入される行の数を変えてみましたTOP
。ID % 16000
すべてのテストに使用しました。以下は、挿入された行を圧縮された行グループセグメントサイズと比較するグラフです。
以下は、ミリ秒単位のCPU時間に挿入された行のグラフです。X軸には異なる開始点があることに注意してください。
行グループセグメントサイズが線形レートで増加し、約1 M行まで少量の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 ║
╚═══════════╩═════════╩═══════════════╩═════════════╝
mod値が1600になるまで、行グループセグメントサイズは、10個の一意の値が追加されるたびに80バイトずつ直線的に増加します。それは興味深い偶然ですBIGINT
伝統的に8バイトを占有し、追加の一意の値ごとにセグメントサイズが8バイト増加です。mod値が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 *一意の値の数が比較的低い圧縮(modの行あたり2バイト<= 65000)で、CPU使用率が低く線形であるように見えます。挿入された行の数が>〜64 *一意の値の数である場合、圧縮率が大幅に向上し、CPU使用率は線形になります。2つの状態間に遷移があり、モデル化するのは簡単ではありませんが、グラフで見ることができます。一意の値ごとに正確に64行を挿入したときに最大CPU使用率が表示されるのは事実ではないようです。むしろ、行グループに挿入できるのは最大1048576行のみであり、一意の値ごとに64行を超えると、CPU使用率と圧縮が大幅に増加します。
以下は、挿入された行の数と一意の行の数が変化したときのCPU時間の変化の等高線図です。上記のパターンを確認できます。
以下は、セグメントが使用する空間の等高線図です。ある時点の後、上で説明したように、はるかに優れた圧縮が見られるようになります。
ここでは、少なくとも2つの異なる圧縮アルゴリズムが機能しているようです。上記を考えると、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 ║
╚════════════╩════════════╩══════════════╩═══════════════╝
スレッドのバランスを取り、行の分布を同じにするためにできるさまざまなトリックがあります。それらの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 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
各並列スレッドでテーブルのスキャンが1回実行されます。
結論として、均等に分布した整数を挿入する場合、一意の各整数が64回以上出現すると非常に高い圧縮率が得られます。これは、使用されている異なる圧縮アルゴリズムが原因である可能性があります。この圧縮を実現するには、CPUのコストが高くなる可能性があります。データの小さな変更は、圧縮された行グループセグメントのサイズの劇的な違いにつながる可能性があります。少なくともこのデータセットについては、最悪のケースを(CPUの観点から)見ることは野生では珍しいと思われます。並列挿入を行うときはさらに見づらくなります。