ClickHouse におけるパーツマージとは何ですか?
ClickHouse が高速なのは、クエリだけでなく insert でも同様です。これは、LSM trees に似た仕組みで動作するストレージ層によるものです。 ① MergeTree engine ファミリーのテーブルへの insert では、ソート済みで不変のデータパーツが作成されます。 ② すべてのデータ処理は、バックグラウンドでのパーツマージにオフロードされます。 その結果、データの書き込みは軽量で、非常に効率的になります。 テーブルごとのパーツ数を制御し、上記の ② を実現するために、ClickHouse はバックグラウンドで小さなパーツを大きなパーツへ継続的にマージし (パーティションごと) 、圧縮サイズがおよそ ~150 GB に達するまでこれを続けます。 次の図は、このバックグラウンドマージのプロセスを概略的に示したものです。
パーツの
merge level は、マージのたびに 1 ずつ増加します。レベル 0 は、そのパーツが新規作成されたもので、まだマージされていないことを意味します。より大きなパーツにマージされたパーツはinactiveとしてマークされ、最終的には設定可能な時間の経過後に削除されます (デフォルトは 8 分) 。時間の経過とともに、これによりマージ済みパーツのツリーが形成されます。これが merge tree テーブルという名前の由来です。
マージの監視
/merges HTTP ハンドラー経由で利用でき、この例のテーブルで発生するすべてのパーツマージを可視化できます。
上のダッシュボードの録画では、最初のデータ挿入から単一パーツへの最終的なマージまで、プロセス全体を確認できます。 ① アクティブなパーツ数。 ② ボックスで視覚的に表したパーツマージ (サイズはパーツの大きさを反映) 。 ③ 書き込み増幅。
並行マージ
各マージスレッドは、次のループを実行します。 ① 次にマージするパーツを決定し、それらをメモリに読み込みます。 ② メモリ内のパーツを、より大きなパーツへマージします。 ③ マージ後のパーツをディスクに書き込みます。 ① に戻る CPU コア数と RAM 容量を増やすことで、バックグラウンドマージのスループットを高められる点に注意してください。
メモリ最適化されたマージ
マージの仕組み
パーツマージは、いくつかのステップで実行されます: ① 解凍と読み込み:マージ対象のパーツに含まれる圧縮済みバイナリカラムファイルを解凍し、メモリに読み込みます。 ② マージ:データを、より大きなカラムファイルへマージします。 ③ 索引作成:マージ後のカラムファイルに対して、新しいスパースプライマリインデックスが生成されます。 ④ 圧縮と保存:新しいカラムファイルと索引は圧縮され、マージ後のデータパーツを表す新しいディレクトリに保存されます。 また、セカンダリのデータスキッピングインデックス、カラム STATISTICS、チェックサム、min-max 索引 などのdata parts 内の追加メタデータも、マージ後のカラムファイルに基づいて再作成されます。ここでは簡略化のため、これらの詳細は省略しています。 ステップ②の仕組みは、使用するMergeTree エンジンによって異なります。これは、エンジンごとにマージの処理方法が異なるためです。たとえば、古くなった行が集計されたり、置き換えられたりすることがあります。前述のとおり、このアプローチではすべてのデータ処理をバックグラウンドマージにオフロードするため、書き込み処理を軽量かつ効率的に保ち、超高速な書き込みを実現できます。 次に、MergeTree family に属する各エンジンのマージの仕組みを簡単に見ていきます。
標準マージ
上図のDDLステートメントは、ソートキーが
(town, street) の MergeTree テーブルを作成します。つまり、ディスク上のデータはこれらのカラムでソートされ、それに応じてスパースプライマリインデックスが生成されることを意味します。
① 圧縮解除され、あらかじめソートされたテーブルのカラムが、テーブルのソートキーで定義された全体のソート順を維持したまま ② マージされ、③ 新しいスパースプライマリインデックスが生成され、④ マージ後のカラムファイルと索引が圧縮されて、ディスク上の新しいデータパーツとして保存されます。
Replacing マージ
上の図の DDL ステートメントは、ソートキー
(town, street, id) を持つ ReplacingMergeTree テーブルを作成しています。これは、ディスク上のデータがこれらのカラム順にソートされ、それに応じてスパースプライマリインデックスが生成されることを意味します。
② のマージ処理は、標準的な MergeTree テーブルと同様に動作し、グローバルなソート順を維持しながら、解凍済みで事前にソートされたカラムを結合します。
ただし、ReplacingMergeTree では同じソートキーを持つ重複行が削除され、その行を含むパートの作成タイムスタンプに基づいて最新の行だけが保持されます。
加算マージ
上の図の DDLステートメントでは、
town をソートキーとする SummingMergeTree テーブルを定義しています。これは、ディスク上のデータがこのカラムでソートされ、それに対応するスパースプライマリインデックスが作成されることを意味します。
② のマージ処理では、ClickHouse は同じソートキーを持つすべての行を 1 つの行に置き換え、数値カラムの値を合計します。
集計マージ
SummingMergeTree テーブルの例は、AggregatingMergeTree テーブルの特化版であり、パートのマージ時に 90+ の任意の集計関数を適用することで、データの自動インクリメンタル変換 を可能にします。
上の図の DDL ステートメントは、
town をソートキーとする AggregatingMergeTree テーブルを作成し、このカラムに基づいてデータがディスク上で順序付けられ、対応するスパースプライマリインデックスが生成されるようにします。
② のマージ中、ClickHouse は同じソートキーを持つすべての行を、部分集計状態 を格納した 1 行に置き換えます (たとえば、avg() に対する sum と count) 。これらの状態により、インクリメンタルなバックグラウンドマージを通じて正確な結果が保証されます。