列ストアインデックスの Column Elimination / Rowgroup Elimination
日本語だと「列の削除」、「行グループの削除」です。
set statistics io on
で、IO の情報から内容を確認します。
列の削除
全ての列を使うクエリと一部の列を使うクエリで比較します。
set statistics io on select max(c1), max(c2), count(c3), max(c4), max(c5), max(c6), min(c7), max(c8), max(c9), max(c10), max(c11), max(c12), max(c13), max(c14), max(c15), max(c16), max(c17), max(c18), max(c19), max(c20), max(c21), max(c22), max(c23), max(c24) from cci_ordered select max(c1), max(c2), count(c3), max(c4), max(c5) from cci_ordered set statistics io off
Warning: Null value is eliminated by an aggregate or other SET operation. (1 row(s) affected) Table 'cci_ordered'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 27656, lob physical reads 0, lob read-ahead reads 0. Table 'cci_ordered'. Segment reads 3, segment skipped 0. (1 row(s) affected) Table 'cci_ordered'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 7757, lob physical reads 0, lob read-ahead reads 0. Table 'cci_ordered'. Segment reads 3, segment skipped 0.
1度データをキャッシュに乗せてから実行したので、physical reads はありませんが、lob logical reads の数が、上と下では結構違います。
全ての列 | 一部の列 |
---|---|
lob logical reads 27656 | lob logical reads 7757 |
これが、Column Elimination(列の削除) です。
ちなみに Segment reads 3, segment skipped 0. って出てますが、これ行グループのことで、列セグメント(column segment) のことではありません。
sys.column_store_segments (Transact-SQL) | Microsoft Docs
の segment_id の説明を見てもらうとわかりますが、「行グループIDのことを指してる、互換性のため列名は segment_id のままにしてる」と書いてます。
要は行グループのことを昔は segment と呼んでたってことですかね。
行グループの除去
全く同じデータを順不同で CCI(Clustered Columnstore Index) を作成した cci テーブルと、
c1 列でソートしてCCIを作成した cci_ordered というテーブルで比較します。
※ちなみにソートした列ストアインデックスを作るにはちょっとしたコツが要ります。
まずはどの条件なら読む行グループが減るかを確認するため、sys.column_store_segments から情報を取ります。
select object_name(p.object_id) as table_name , c.name as column_name , css.segment_id, css.row_count, css.min_data_id, css.max_data_id from sys.column_store_segments css left join sys.partitions p on p.hobt_id = css.hobt_id and p.partition_id = css.partition_id left join sys.indexes ind on p.index_id = ind.index_id and p.object_id = ind.object_id left join sys.index_columns indc on indc.object_id = ind.object_id and indc.index_id = ind.index_id and indc.index_column_id = css.column_id left join sys.columns c on c.column_id = indc.column_id and c.object_id = p.object_id where object_name(p.object_id) in ('cci', 'cci_ordered') and c.name = 'c1' order by p.object_id, css.column_id, css.segment_id
min_data_id、max_data_id に注目し、cci_ordered は1個の行グループ、cci は3個の行グループになる条件を付けます。
今回は c1 <= 1028864 にします。
これで、cci テーブルは、全行グループが、cci_orderedテーブルは、segment_id = 0 の 1行グループが読み込まれるはずです。
set statistics io on select max(c1), max(c2), count(c3), max(c4), max(c5) from cci where c1 <= 1028864 select max(c1), max(c2), count(c3), max(c4), max(c5) from cci_ordered where c1 <= 1028864 set statistics io off
(1 row(s) affected) Table 'cci'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 7767, lob physical reads 0, lob read-ahead reads 0. Table 'cci'. Segment reads 3, segment skipped 0. (1 row(s) affected) Table 'cci_ordered'. Scan count 1, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 3773, lob physical reads 0, lob read-ahead reads 0. Table 'cci_ordered'. Segment reads 1, segment skipped 2.
lob logical reads と Segement reads に注目します。
順不同 (cci) | ソート済 (cci_ordered) |
---|---|
lob logical reads 7767 | lob logical reads 3773 |
Table ‘cci’. Segment reads 3, segment skipped 0. | Table ‘cci_ordered’. Segment reads 1, segment skipped 2. |
ソート済の cci_ordered は segment skiped 2 とでて、logical reads の数も減っています。
これが RowGroup Elimination(行グループの削除)です。
ソート済の列ストアインデックスを作成する
列ストア インデックスのクエリ パフォーマンス
に記載されていますが、
- 時系列でデータを追加する
- 行ストアクラスター化インデックスを作成し、データを並び替えたあとに列ストアインデックスに差し替える(MAXDOP=1 で実行)
2番目の方法が曲者で、MAXDOP=1 で動かさないとパラレルで行ストアが作成されるます。
そのため複数の行ストアにデータが分散してしまい、ソートが効かなくなってしまいます。
今回使った cci_ordered ではこんな感じで作成してます。
create clustered index cci_ordered_index on cci_ordered(c1) create clustered columnstore index cci_ordered_index on cci_ordered with (drop_existing=ON,MAXDOP=1)
ただ MAXDOP=1 なので、時間がかかる!
列ストアを使う位デカいテーブルなのに MAXDOP=1は時間かかるよーって意見が Connect にも上がってます。
Multi-threaded rebuilds of Clustered Columnstore Indexes break the sequence of pre-sorted segment ordering (Order Clustering) | Microsoft Connect
2014 の時に登録されて、次のリリースで直すみたいな感じでしたが、2016 でも直ってませんね。vNext だと直ってるのかな?