読者です 読者をやめる 読者になる 読者になる

SQL Server 2016 列ストアの更新について

OLTP 的な、1件更新について書きます。

sys.internal_partitions と sys.column_store_row_groups で見ると更新処理の動きが見えてきます。
以降このクエリで情報を見ていきます。

-- <tablename> は適宜読み替えてください。
-- internal_partition
select object_name(object_id), internal_object_type, internal_object_type_desc, row_group_id, rows 
from sys.internal_partitions
where object_name(object_id) = '<tablename>'
order by object_id, internal_object_type

-- column_store_row_gourps
select object_name(object_id), row_group_id, state, state_description, total_rows, deleted_rows, size_in_bytes
from sys.column_store_row_groups
where object_name(object_id) = '<tablename>'
order by object_id, row_group_id

クラスター化列ストアインデックス Clustered Columnstore Index (CCI) について

2014 から変わったという記述を見つけれなかったので、多分 2014 と同じ。
クラスター化列ストア インデックスの使用

ざっくりとした動きはこちら

  • INSERT
    • デルタストアに INSERT
  • DELETE
    • 削除ビットマップ を立てる
  • UPDATE
    • 列ストアにデータがあるときは、上の DELETE、INSERT の操作
    • デルタストアにデータがあるときは、UPDATE

実際に試してみましょう。
まず更新前の状態です。200万行が列ストアとして圧縮済です。
f:id:odashinsuke:20170331225723p:plain

INSERT

insert into cci (c1) values (-1)

f:id:odashinsuke:20170331225803p:plain
internal_partitions には、COLUMN_STORE_DELTA_STORE が追加されていますね。
column_store_row_gourps には、OPEN な新しい行グループが増えて、そこに1行追加されたのがわかります。

DELETE

delete from cci where c1 = 1000000

f:id:odashinsuke:20170331155318p:plain
COLUMN_STORE_DELETE_BITMAP の rows が 1になり、
データがいたと思われる行グループは、deleted_rows が 1 になりました。

UPDATE

update cci set c2 = 100 where c1 = 1000

f:id:odashinsuke:20170331225851p:plain
DELETE と INSERT の動きを合わせた結果になってますね。

最後に、デルタストアにある行を DELETE してみます。

delete from cci where c1 = -1

f:id:odashinsuke:20170331225904p:plain
これは、デルタストアの行が減ってることが確認出来ます。

クラスター化列ストアインデックス Nonclustered Columnstore Index (NCCI) について

2016 から更新可能に。
Real-Time Operational Analytics: DML operations and nonclustered columnstore index (NCCI) in SQL Server 2016 | SQL Server Database Engine Blog

  • INSERT
    • デルタストアに INSERT
  • DELETE
    • 削除バッファに INSERT
  • UPDATE
    • DELETE、INSERT の操作(デルタストアにあっても UPDATEしない。*1 )

NCCI の場合は、削除ビットマップ ではなく、削除バッファーに行くのが違いですね。
削除バッファーから削除ビットマップへの変換は、バックグランドで定期的に行われるか、インデックスのメンテナンスのタイミングになります。

こちらも実際に試してみましょう。
まず更新前の状態です。200万行が列ストアとして圧縮済です。
f:id:odashinsuke:20170331230129p:plain
CCI との違いは、COLUMN_STORE_DELETE_BUFFER があることですね。

INSERT

insert into ncci (c1) values (-1)

f:id:odashinsuke:20170331230146p:plain
CCI と同様の動きですね。

DELETE

delete from ncci where c1 = 1000000

f:id:odashinsuke:20170331230159p:plain
CCI とは異なり、COLUMN_STORE_DELETE_BUFFER の rows が 1になり、DELETE_BITMAP の方は変化なしです。
行グループも、deleted_rows が増えていません。

UPDATE

update ncci set c2 = 100 where c1 = 1000

f:id:odashinsuke:20170331230213p:plain
CCI と同様、DELETE と INSERT の動きを合わせた結果になってますね。
CCI と違うのは、DELETE が DELETE_BUFFER に入るとこです。

デルタストアにある行を DELETE してみます。

delete from ncci where c1 = -1

f:id:odashinsuke:20170331230239p:plain
CCI と同様の動きですね。

最後は インデックスのメンテナンスをしてみましょう。

alter index ncci_index ON ncci
REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);  

f:id:odashinsuke:20170331231457p:plain
DELETE_BUFFER から DELETE_BITMAP に変換されてますね。
DELETE_BITMAP に変換されたので、deleted_rows の値が増えてます。
あとは OPEN だった行グループが圧縮されて、新しい行グループになり、圧縮前のデルタストア(行グループ)は廃棄(TOMBSTONE)状態になりました。
TOMBSTONE の行グループはしばらくすると消えてなくなります。

フィルター付き NCCI

NCCI は、フィルター条件を付けれるので、フィルターの条件にマッチする行の追加/更新/削除 は上と同じ動きをしますが、
フィルターの条件外の行の場合は、列ストアインデックスでは無いので列ストアとしての更新処理は行われません。

*1:解釈間違ってたらゴメン原文:There is no in place update even when the row is found in delta rowgroup.