パーツマージ
ClickHouseにおけるパートマージとは何ですか?
ClickHouseはクエリだけでなく、挿入処理においても高速です。その理由は、ストレージレイヤーがLSMツリーのように動作するためです。
① MergeTreeエンジンファミリーのテーブルへの挿入は、ソートされた不変のデータパーツを生成します。
② すべてのデータ処理はバックグラウンドパートマージにオフロードされます。
これにより、データの書き込みが軽量かつ非常に効率的になります。
各テーブルの^^parts^^の数を制御し、②を実装するために、ClickHouseはバックグラウンドで小さな^^parts^^を大きな^^parts^^に継続的にマージします(パーティションごと)。これは、圧縮されたサイズが約~150 GBに達するまで行われます。
以下の図は、このバックグラウンドマージプロセスを示しています。

パートのmerge levelは、追加のマージごとに1ずつインクリメントされます。0のレベルは、そのパートが新しく、まだマージされていないことを意味します。大きな^^parts^^にマージされた^^parts^^は非アクティブとしてマークされ、最終的には設定可能な時間(デフォルトで8分)経過後に削除されます。時間が経つにつれ、これはマージされた^^parts^^のツリーを作成します。したがって、マージツリーテーブルという名称が付けられています。
マージの監視
テーブルパーツとは?の例で、ClickHouseはすべてのテーブル^^parts^^をpartsシステムテーブルで追跡していることを示しました。以下のクエリを使用して、例のテーブルのアクティブな各パートのマージレベルと保存されている行の数を取得しました:
以前に文書化されたクエリの結果は、例のテーブルが4つのアクティブな^^parts^^を持ち、それぞれが最初に挿入された^^parts^^の単一のマージから作成されたことを示しています:
クエリを実行すると、現在クエリは4つの^^parts^^が単一の最終パートにマージされたことを示しています(テーブルにさらなる挿入がない限り)。
ClickHouse 24.10では、組み込みのモニタリングダッシュボードに新しいマージダッシュボードが追加されました。OSSとCloudの両方で/merges HTTPハンドラー経由で利用でき、例のテーブルのすべてのパートマージを可視化するために使用できます:

上記の記録されたダッシュボードは、最初のデータ挿入から単一パートへの最終マージまでの全プロセスをキャプチャしています:
① アクティブな^^parts^^の数。
② ボックスで視覚的に表現されたパートマージ(サイズはパートサイズを反映)。
③ 書き込み増幅。
同時マージ
単一のClickHouseサーバーは、同時パートマージを実行するためにいくつかのバックグラウンドマージスレッドを使用します:

各マージスレッドはループを実行します:
① 次にマージする^^parts^^を決定し、これらの^^parts^^をメモリにロードします。
② メモリ内の^^parts^^を大きなパートにマージします。
③ マージされたパートをディスクに書き込みます。
①に戻る
CPUコアの数とRAMのサイズを増加させることで、バックグラウンドマージのスループットを増加させることができることに注意してください。
メモリ最適化されたマージ
ClickHouseは、前の例に示されているように、マージされるすべての^^parts^^を一度にメモリにロードするわけではありません。いくつかの要因に基づき、メモリ消費を削減するため(マージ速度を犠牲にして)、いわゆる垂直マージでは、^^parts^^をチャンクのブロック単位でロードしマージします。
マージメカニクス
以下の図は、ClickHouseにおける単一のバックグラウンドマージスレッドが^^parts^^をどのようにマージするかを示しています(デフォルトでは垂直マージは行われません):

パートマージは以下のいくつかのステップで実行されます:
① 解凍とロード:マージされる^^parts^^から圧縮されたバイナリカラムファイルが解凍され、メモリにロードされます。
② マージ:データが大きなカラムファイルにマージされます。
③ インデクシング:マージされたカラムファイル用の新しいsparse primary indexが生成されます。
④ 圧縮とストレージ:新しいカラムファイルとインデックスが圧縮され、新たにマージされたデータパートを表すディレクトリに保存されます。
二次データスキッピングインデックス、カラム統計、チェックサム、最小-最大インデックスなど、データパーツのメタデータも、マージされたカラムファイルに基づいて再作成されます。簡潔さを保つために、これらの詳細は省略しました。
ステップ②のメカニクスは、特定のMergeTreeエンジンの使用に依存します。異なるエンジンはマージの処理を異なって扱います。たとえば、行は集約または置き換えされる場合があります。このアプローチにより、すべてのデータ処理をバックグラウンドマージにオフロードし、書き込み操作を軽量かつ効率的に保つことによって非常に高速な挿入を実現しています。
次に、^^MergeTree^^ファミリーの特定のエンジンのマージメカニクスを簡単に概説します。
標準マージ
以下の図は、標準のMergeTreeテーブルで^^parts^^がどのようにマージされるかを示しています:

図のDDLステートメントは、^^sorting key^^ (town, street)を持つMergeTreeテーブルを作成します。これは、ディスク上のデータがこれらの列によってソートされ、対応するスパースプライマリインデックスが生成されることを意味します。
① 解凍された前もってソートされたテーブルカラムが、② テーブルの^^sorting key^^によって定義されたグローバルなソート順を保持しつつマージされ、③ 新しいスパースプライマリインデックスが生成され、④ マージされたカラムファイルとインデックスが圧縮され、新たなデータパートとしてディスクに保存されます。
置き換えマージ
ReplacingMergeTreeテーブルのパートマージは、標準マージと類似していますが、各行の最新バージョンのみが保持され、古いバージョンは破棄されます:

図のDDLステートメントは、^^sorting key^^ (town, street, id)を持つReplacingMergeTreeテーブルを作成します。これは、ディスク上のデータがこれらの列によってソートされ、対応するスパースプライマリインデックスが生成されることを意味します。
② マージは、解凍された前もってソートされたカラムを保持しつつ、標準のMergeTreeテーブルと同様に実行されます。
しかし、ReplacingMergeTreeは同じ^^sorting key^^を持つ重複行を削除し、そのパートの作成タイムスタンプに基づいて最も新しい行のみを保持します。
合計マージ
数値データは、SummingMergeTreeテーブルの^^parts^^のマージ中に自動的に集約されます:

図のDDLステートメントは、townを^^sorting key^^として定義するSummingMergeTreeテーブルを作成します。これは、ディスク上のデータがこのカラムによってソートされ、対応するスパースプライマリインデックスが作成されることを意味します。
② マージステップでは、ClickHouseは同じ^^sorting key^^を持つすべての行を1行に置き換え、数値カラムの値を合計します。
集約マージ
上記のSummingMergeTreeテーブルの例は、AggregatingMergeTreeテーブルの専門化されたバリアントであり、パートマージ中に任意の90+集約関数を適用することにより、自動的な増分データ変換を許可します:

図のDDLステートメントは、townを^^sorting key^^として持つAggregatingMergeTreeテーブルを作成します。これにより、データがディスク上でこのカラムによって順序付けられ、対応するスパースプライマリインデックスが生成されます。
② マージ中、ClickHouseは同じ^^sorting key^^を持つすべての行を置き換え、部分集約状態を格納する1行に置き換えます(例:avg()のためのsumとcount)。これらの状態は、増分バックグラウンドマージを通じて正確な結果を保証します。