説明
CollapsingMergeTree エンジンは MergeTree を継承し、
マージ処理中に行を折りたたむロジックを追加したものです。
CollapsingMergeTree テーブルエンジンは、
特別なフィールド Sign を除き、ソートキー (ORDER BY) 内のすべてのフィールドが同一で、
Sign の値が 1 または -1 である行のペアを非同期に削除 (折りたたみ) します。
反対の値を持つ Sign のペアが存在しない行は保持されます。
詳細については、このドキュメントの 折りたたみ セクションを参照してください。
このエンジンを使用すると、ストレージ使用量を大幅に削減でき、
その結果として
SELECT クエリの効率が向上する可能性があります。パラメータ
Sign パラメータを除き、
MergeTree のものと同じ意味を持ちます。
Sign— 行タイプを表すカラム名です。1は「状態行」、-1は「取消行」を表します。型: Int8.
テーブルの作成
- クエリパラメータの説明については、クエリの説明を参照してください。
CollapsingMergeTreeテーブルを作成する際は、MergeTreeテーブルを作成する場合と同様に、同じクエリ句が必要です。
折りたたみ
データ
Sign を使用します。
Sign=1の場合、その行は 状態行、つまり 現在の有効な状態を表すフィールドを含む行 を意味します。Sign=-1の場合、その行は 取消行、つまり 同じ属性を持つオブジェクトの状態を打ち消すために使用される行 を意味します。
Sign を除き、“canceled” 行のソートキーのフィールドをすべてコピーする必要があります。
上の2行目には現在の状態が入っています。
必要なのはユーザーアクティビティの最新の状態だけなので、以下に示すように、元の 状態行 と挿入した 取消行
行は削除でき、これによりオブジェクトの無効な (古い) 状態が折りたたまれます。
CollapsingMergeTree は、データパーツのマージ時に、まさにこの折りたたみ動作を実行します。
各変更に対して 2 行が必要になる理由
については、Algorithm の段落でさらに説明しています。
- データを書き込むプログラムは、それを打ち消せるように、オブジェクトの状態を覚えておく必要があります。取消行には、状態行 のソートキー フィールドのコピーと、逆の
Signを含める必要があります。これにより保存領域の初期サイズは増えますが、データを高速に書き込めます。 - カラム内の長大化する配列は、書き込み負荷の増加によってエンジンの効率を低下させます。データが単純であるほど、効率は高くなります。
SELECTの結果は、オブジェクトの変更履歴の整合性に大きく左右されます。挿入するデータは、正確に準備してください。データに不整合があると、予測不能な結果になることがあります。たとえば、session depth のような非負のメトリクスに負の値が入ることがあります。
アルゴリズム
ORDER BY) を持つ連続した行の各グループは、最大2行までに減らされます。
つまり、Sign = 1 の「状態行」と Sign = -1 の「取消行」です。
言い換えると、ClickHouseではエントリが折りたたまれます。
ClickHouseは、生成された各データパーツについて次を保存します。
さらに、「状態行」が「取消行」より2行以上多い場合、
または「取消行」が「状態行」より2行以上多い場合、マージは継続されます。
ただし、ClickHouseはこの状況を論理エラーとして扱い、server log に記録します。
このエラーは、同じデータが複数回挿入された場合に発生することがあります。
したがって、折りたたみによって統計の計算結果が変わるべきではありません。
変更は徐々に折りたたまれ、最終的にはほぼすべてのオブジェクトで最後の状態だけが残ります。
マージアルゴリズムでは、
同じソートキーを持つすべての行が同じ結果データパーツ内、さらには同じ物理サーバー上に配置されるとは限らないため、
Signカラムが必要です。
ClickHouseは複数のスレッドでSELECTクエリを処理するため、結果内の行の順序を予測できません。
CollapsingMergeTreeテーブルから完全に「折りたたまれた」データを取得する必要がある場合は、集約が必要です。
折りたたみを完了するには、GROUP BY句と、符号を考慮した集約関数を使うクエリを記述します。
たとえば、件数を計算するには count() の代わりに sum(Sign) を使用します。
何らかの合計を計算するには、以下の例のように、sum(x) の代わりに HAVING sum(Sign) > 0 と組み合わせて sum(Sign * x) を使用します。
集約 count、sum、avg はこの方法で計算できます。
オブジェクトに少なくとも1つの未折りたたみの状態があれば、集約 uniq も計算できます。
一方、集約 min と max は計算できません。
これは、CollapsingMergeTree が折りたたまれた状態の履歴を保存しないためです。
集約せずにデータを取り出す必要がある場合
(たとえば、最新の値が特定の条件に一致する行が存在するかどうかを確認する場合) 、
FROM句にFINAL修飾子を使用できます。結果を返す前にデータがマージされます。
CollapsingMergeTree では、各キーについて最新の状態行のみが返されます。例
使用例
CollapsingMergeTree を使用して、テーブル UAct を作成しましょう:
INSERT クエリを 2 回使用します。
1 回のクエリでデータを挿入すると、ClickHouse が作成するデータパーツは 1 つだけで、その後マージは実行されません。
INSERT クエリによって、2つのデータパーツが作成されました。
SELECT クエリは2つのスレッドで実行されたため、行の順序はランダムになっています。
ただし、データパーツのマージがまだ行われていないため、折りたたみは発生していません
また、ClickHouse は予測できないタイミングでバックグラウンドでデータパーツをマージするためです。
そのため、集約が必要になります。
これには sum
集約関数と HAVING 句を使用します。
FROM 句で FINAL 修飾子を使用することもできます。
この方法でデータを選択するのは効率が低く、スキャンするデータ量が多い場合 (数百万行) には推奨されません。
別のアプローチの例
Sign カラムを使わなくても、合計時に前のバージョンの行を相殺できる負の値を指定できます。
この例では、以下のサンプルデータを使用します。
PageViews と Duration のデータ型を変更する必要があります。
そのため、collapsingMergeTree を使用してテーブル UAct を作成する際に、これらのカラムの型を UInt8 から Int16 に変更します。