更新戦略の選択
- insert によって更新を処理する 専用のテーブルエンジン を使用する
UPDATE ... SETやALTER TABLE ... UPDATEのような 宣言的な更新 ステートメントを使用する
専用のテーブルエンジンを使う場合
| エンジン | 構文 | 使用する場合 |
|---|---|---|
| ReplacingMergeTree | ENGINE = ReplacingMergeTree | 大量のデータを更新する場合に使用します。このテーブルエンジンは、マージ時のデータの重複排除に最適化されています。 |
| CoalescingMergeTree | ENGINE = CoalescingMergeTree | データが断片的に到着し、行全体の置き換えではなく、カラム単位での統合が必要な場合に使用します。 |
| CollapsingMergeTree | ENGINE = CollapsingMergeTree(Sign) | 個々の行を頻繁に更新する場合や、時間とともに変化するオブジェクトの最新状態を維持する必要があるシナリオで使用します。たとえば、ユーザーアクティビティや記事の統計の追跡です。 |
FINAL キーワードを使用する必要があります。
ほかにもエンジンの種類はありますが、これらが最も一般的に使われます。
宣言的更新を使用するタイミング
UPDATE ステートメントは、重複排除ロジックを管理する複雑さがなく、単純な更新処理ではより扱いやすい場合があります。ただし一般に、専用エンジンを使う方法と比べると、比較的少ない行を低頻度で更新する用途に向いています。
| Method | Syntax | When to use |
|---|---|---|
| 論理更新 | UPDATE [table] SET ... WHERE | ほとんどのケースではこちらを使用してください。特に、アプリケーションやワークフローの一部として、頻繁に小規模な UPDATE (テーブルの最大約 10% まで) を実行する場合に適しています。たとえば、ユーザーが自分のイベント履歴の削除を希望しており、そのイベントが多数のユーザーを含むマルチテナントテーブル全体に分散しているケースです。この方法では、カラム全体を書き換えることなく、すぐに反映するためのパッチパートが作成されます。SELECT クエリにオーバーヘッドは加わりますが、レイテンシは予測しやすくなります。 |
| 更新ミューテーション | ALTER TABLE [table] UPDATE | より大規模なデータ管理を行う場合に使用してください。特に、更新対象がテーブルのパーティション化と合っている場合に適しています。たとえば、月ごとにパーティション化されたテーブルで、ある月に含まれるすべての行のカラムを更新する必要があるケースです。 |
専用のテーブルエンジンを使った更新
ReplacingMergeTree
ReplacingMergeTree は、バックグラウンドマージ時に同じソートキーを持つ行を重複排除し、最新バージョンのみを保持します。
FINAL 修飾子または同等のクエリロジックを使用する必要があります。FINAL 修飾子を使うと、データに応じてクエリのオーバーヘッドが 21~550% 増加します。
ReplacingMergeTree ではソートキーの値は更新できません。また、論理削除用の Deleted カラムもサポートしています。
詳細: ReplacingMergeTree ガイド | ReplacingMergeTree リファレンス
CoalescingMergeTree
Nullable にする必要があります。ReplacingMergeTree と同様に、正しく統合された結果を得るには FINAL を使用してください。
このエンジンは ClickHouse 25.6 から利用できます。
詳細: CoalescingMergeTree
CollapsingMergeTree
CollapsingMergeTree は Sign カラムを使って、マージ時に ClickHouse が行をどのように処理するかを指定します。Sign カラムに -1 が挿入されると、その行は対応する +1 の行と対になったときに折りたたまれ (削除され) ます。更新対象の行は、テーブル作成時に ORDER BY 句で使用するソートキーに基づいて識別されます。
ReplacingMergeTreeとは異なり、CollapsingMergeTreeではソートキーの値を変更できます。金融取引やゲーム状態の追跡など、取り消しを前提とした可逆的な操作に適しています。
上記の更新方法では、打ち消し用の行を挿入するために、アプリケーション側でクライアントの状態を保持しておく必要があります。これは ClickHouse の観点では最も効率的ですが、大規模環境では扱いが複雑になることがあります。また、正しい結果を得るには、クエリでも符号の乗算を伴う集約が必要です。
宣言的更新
ミューテーション
ALTER TABLE ... UPDATE) は、WHERE 式に一致する行を含むすべてのパーツを再書き込みします。
WHERE 式に一致するすべてのパーツを書き換えます。
このプロセスにはアトミック性がありません。
パーツは準備でき次第、変更後のパーツに置き換えられるため、ミューテーションの実行中に開始された SELECT クエリでは、すでにミューテーションされたパーツのデータと、まだミューテーションされていないパーツのデータの両方が見えることになります。
進行状況は system.mutations テーブルで確認できます。
詳細: ALTER TABLE UPDATE
オンザフライミューテーション
ALTER TABLE ... UPDATE によるミューテーションでは、変更後の値がクエリ結果に反映されるまで、バックグラウンドプロセスによってミューテーションが適用されるのを待つ必要がある場合があります。
ClickHouse では、“オンザフライミューテーション” によってこの動作を変更できます。
オンザフライミューテーションを有効にすると、更新された行は即座に更新済みとしてマークされ、後続の SELECT クエリでは変更後の値が自動的に返されます。
オンザフライミューテーションは、クエリレベルの設定 apply_mutations_on_fly を有効にすることで、MergeTree ファミリーのテーブルで使用できます。
例
例
テーブルを作成し、いくつかのミューテーションを実行してみましょう。新しいテーブルに対してクエリを実行した時点では、行の値はまだ更新されていないことに注意してください。次に、オンザフライミューテーションを有効にするとどうなるかを見てみましょう。これで
SELECT クエリで更新結果を確認してみましょう。SELECT クエリは、ミューテーションが適用されるのを待つことなく、直ちに正しい結果を返します。パフォーマンスへの影響
SELECT クエリの実行時にのみ適用されます。ただし、ミューテーションのマテリアライズ自体は引き続きバックグラウンドで非同期に行われており、これは負荷の高い処理である点に注意してください。
一定の期間にわたって、投入されるミューテーション数がバックグラウンドで処理されるミューテーション数を継続的に上回ると、適用が必要な未マテリアライズのミューテーションのキューは増え続けます。その結果、最終的に SELECT クエリのパフォーマンスが低下します。
未マテリアライズのミューテーションが際限なく増え続けるのを防ぐため、設定 apply_mutations_on_fly は、number_of_mutations_to_throw や number_of_mutations_to_delay などの他の MergeTree レベルの設定とあわせて有効にすることを推奨します。
サブクエリおよび非決定論的関数のサポート
mutations_max_literal_size_to_replace で制御されます) 。また、非決定論的関数については、定数となるもののみがサポートされます (例: 関数 now()) 。
これらの動作は、次の設定で制御されます。
| 設定 | 説明 | デフォルト |
|---|---|---|
mutations_execute_nondeterministic_on_initiator | true の場合、非決定論的関数はイニシエーターレプリカで実行され、UPDATE および DELETE クエリ内でリテラルに置き換えられます。 | false |
mutations_execute_subqueries_on_initiator | true の場合、スカラーサブクエリはイニシエーターレプリカで実行され、UPDATE および DELETE クエリ内でリテラルに置き換えられます。 | false |
mutations_max_literal_size_to_replace | UPDATE および DELETE クエリ内で置き換える、シリアライズ済みリテラルの最大サイズ (バイト単位) 。 | 16384 (16 KiB) |
論理更新
UPDATE 構文を使用し、マージを待たずにパッチパートを即座に作成します。更新された値は、パッチの適用によって SELECT クエリですぐに参照できますが、物理的に反映されるのは後続のマージ時のみです。そのため、論理更新は、予測可能なレイテンシで少数の行 (テーブルの約 10% まで) を更新するのに最適です。ベンチマークでは、ミューテーションと比べて最大 23 倍高速になることが示されています。
その一方で、SELECT クエリではパッチの適用に伴うオーバーヘッドが発生し、パッチパートはパーツ数の上限にもカウントされます。約 10% のしきい値を超えると、読み取り時のパッチ適用オーバーヘッドが比例して増加するため、より大規模な更新では同期ミューテーションのほうが効率的です。
詳細: Lightweight UPDATE
オンザフライミューテーション
オンザフライミューテーションは、行を更新した際に、その後のSELECTクエリでバックグラウンド処理の完了を待たずに変更後の値が自動的に返される仕組みです。これにより、通常のミューテーションにおけるアトミック性の制約を実質的に解消できます。
SELECT クエリの両方で、apply_mutations_on_fly = 1 設定を有効にする必要があります。ミューテーションの条件は ClickHouse Keeper に保存され、Keeper はそれらをすべてメモリ内に保持し、クエリ実行時にオンザフライで適用します。
データの更新には引き続きミューテーションが使用される点に注意してください。違うのは、すぐには実体化されないということだけです。ミューテーションは非同期プロセスとしてバックグラウンドで引き続き適用され、通常のミューテーションと同じ大きなオーバーヘッドが発生します。また、この操作で使用できる式にも制限があります (詳細を参照) 。
詳しくは: On-the-fly mutations
比較のまとめ
| 方法 | クエリの低下率 | メモリオーバーヘッド | 備考 |
|---|---|---|---|
| Mutations | 基準 | 基準 | 完了後はフルスピード。データは物理的に書き換えられる |
| オンザフライミューテーション | 可変 | 可変 | 即座に反映される。更新が多数蓄積すると性能が低下する |
| 論理更新 | 7~18% (平均約12%) | +20~210% | クエリ効率が最も高い。テーブルの10%以下を更新する場合に最適 |
ReplacingMergeTree + FINAL | 21~550% (平均約280%) | 基準の20~200倍 | すべての行バージョンを読み取る必要がある。クエリのオーバーヘッドが最も大きい |
CoalescingMergeTree + FINAL | ReplacingMergeTreeと同程度 | ReplacingMergeTreeと同程度 | カラム単位の coalescing により同程度のオーバーヘッドが追加される |
| CollapsingMergeTree | 集約に依存 | 集約に依存 | オーバーヘッドはクエリの複雑さに依存する |