軽量 UPDATE ステートメント
Lightweight update は現在ベータ版です。 問題が発生した場合は、ClickHouse リポジトリ に issue を作成してください。
lightweight な UPDATE ステートメントは、filter_expr という式に一致するテーブル [db.]table 内の行を更新します。
このステートメントは、データパーツ内の列全体を書き換える重量級の処理である ALTER TABLE ... UPDATE クエリと対比して「lightweight update」と呼ばれます。
これは MergeTree テーブルエンジンファミリーでのみ利用可能です。
filter_expr は UInt8 型でなければなりません。このクエリは、filter_expr が非ゼロの値を取る行について、指定された列の値を対応する式の評価結果に更新します。
値は CAST 演算子を使用して列の型にキャストされます。プライマリキーまたはパーティションキーの計算に使用されている列の更新はサポートされていません。
例
軽量な更新はデータを即時には更新しない
軽量な UPDATE は パッチパーツ (patch parts) を用いて実装されています。パッチパーツは、更新対象の列と行のみを含む特殊な種類のデータパーツです。
軽量な UPDATE はパッチパーツを作成しますが、ストレージ上の元のデータはすぐに物理的に書き換えられるわけではありません。
更新処理は INSERT ... SELECT ... クエリに似ていますが、UPDATE クエリはパッチパーツの作成が完了するまで待機してから結果を返します。
更新された値は次のとおりです:
- パッチの適用により
SELECTクエリで即座に参照可能になります - 後続のマージおよびミューテーション時にのみ物理的にマテリアライズされます
- すべてのアクティブなパーツでパッチがマテリアライズされると自動的にクリーンアップされます
軽量アップデートの要件
軽量アップデートは、MergeTree、ReplacingMergeTree、CollapsingMergeTree エンジンおよびそれらの Replicated と Shared バージョンでサポートされています。
軽量アップデートを使用するには、テーブル設定 enable_block_number_column および enable_block_offset_column により _block_number および _block_offset カラムのマテリアライズを有効にする必要があります。
軽量な削除
軽量な DELETE クエリは、ALTER UPDATE ミューテーションではなく、軽量な UPDATE として実行できます。軽量な DELETE の実装は、lightweight_delete_mode の設定によって制御されます。
パフォーマンスに関する考慮事項
軽量アップデートの利点:
- アップデートのレイテンシは、
INSERT ... SELECT ...クエリのレイテンシと同程度 - データパーツ内の列全体ではなく、更新された列と値のみが書き込まれる
- 現在実行中のマージやミューテーションの完了を待つ必要がないため、アップデートのレイテンシを予測しやすい
- 軽量アップデートは並列実行が可能
想定されるパフォーマンスへの影響:
- パッチを適用する必要がある
SELECTクエリにはオーバーヘッドが発生する - パッチを適用すべきデータパーツ内の列にはスキップインデックスが使用されない。テーブルにパッチパーツが存在する場合は、パッチを適用する必要がないデータパーツであってもプロジェクションは使用されない。
- ごく小さいアップデートを高頻度で行うと「too many parts」エラーにつながる可能性がある。
WHERE句内の単一のIN句にアップデート対象の ID をまとめるなどして、複数のアップデートを 1 つのクエリにバッチ処理することが推奨される - 軽量アップデートは、テーブル全体の約 10% 程度までの少量の行を更新することを想定して設計されている。より多くの行を更新する必要がある場合は、
ALTER TABLE ... UPDATEミューテーションを使用することが推奨される
同時実行操作
軽量な更新は、重いミューテーションとは異なり、現在実行中のマージやミューテーションの完了を待ちません。
同時に行われる軽量更新の一貫性は、update_sequential_consistency および update_parallel_mode の設定によって制御されます。
更新権限
UPDATE には ALTER UPDATE 権限が必要です。特定のユーザーに対して特定のテーブルで UPDATE ステートメントを有効にするには、以下を実行します。
実装の詳細
パッチパーツは通常のパーツと同じ構造ですが、更新されたカラムと、いくつかのシステムカラムのみを含みます:
_part- 元のパーツの名前_part_offset- 元のパーツ内の行番号_block_number- 元のパーツ内での行のブロック番号_block_offset- 元のパーツ内での行のブロックオフセット_data_version- 更新データのデータバージョン(UPDATEクエリに割り当てられたブロック番号)
平均すると、パッチパーツ内の更新された 1 行あたり約 40 バイト(非圧縮データ)のオーバーヘッドが発生します。
システムカラムは、更新すべき元のパーツ内の行を見つけるのに役立ちます。
システムカラムは、パッチパーツを適用する必要がある場合に読み取り時に追加される、元のパーツ内の仮想カラムと関連しています。
パッチパーツは _part および _part_offset でソートされます。
パッチパーツは、元のパーツとは異なるパーティションに属します。
パッチパーツのパーティション ID は patch-<hash of column names in patch part>-<original_partition_id> です。
したがって、含まれるカラムが異なるパッチパーツは、異なるパーティションに保存されます。
例えば、SET x = 1 WHERE <cond>、SET y = 1 WHERE <cond>、SET x = 1, y = 1 WHERE <cond> という 3 つの更新は、3 つの異なるパーティションに 3 つのパッチパーツを作成します。
パッチパーツ同士をマージすることで、SELECT クエリで適用されるパッチの数を減らし、オーバーヘッドを削減できます。パッチパーツのマージには、バージョンカラムとして _data_version を用いた replacing マージアルゴリズムが使用されます。
したがって、パッチパーツは常に、そのパーツ内の各更新行について最新バージョンのみを保持します。
ライトウェイトアップデートは、現在実行中のマージやミューテーションの終了を待たず、常に現在のデータパーツのスナップショットを使用してアップデートを実行し、パッチパーツを生成します。 そのため、パッチパーツの適用には 2 つのケースが存在します。
例えば、パーツ A を読み取る場合、パッチパーツ X を適用する必要があります:
XがパーツA自体を含む場合。これは、UPDATEが実行された時点でAがマージに参加していなかった場合に発生します。XがパーツBとCを含み、それらがパーツAによってカバーされている場合。これは、UPDATEが実行されたときに、マージ (B,C) ->Aが実行中だった場合に発生します。
これら 2 つのケースに対して、それぞれ次の 2 通りのパッチパーツ適用方法があります:
- ソートされたカラム
_part,_part_offsetによるマージを使用する。 _block_number,_block_offsetカラムによるジョインを使用する。
ジョインモードはマージモードよりも低速で、より多くのメモリを必要としますが、利用頻度は低くなります。
関連コンテンツ
ALTER UPDATE- 負荷の大きいUPDATE操作- 軽量な
DELETE- 軽量なDELETE操作