VersionedCollapsingMergeTree
このエンジンは以下を可能にします:
- 継続的に変化するオブジェクトの状態を迅速に書き込むこと。
- 古いオブジェクトの状態をバックグラウンドで削除します。これにより、ストレージのボリュームが大幅に減少します。
詳細はセクション Collapsing をご覧ください。
このエンジンは MergeTree を継承し、データパーツのマージアルゴリズムに行の縮小に関するロジックを追加します。 VersionedCollapsingMergeTree
は CollapsingMergeTree と同じ目的を果たしますが、複数のスレッドでデータを任意の順序で挿入することを可能にする異なる縮小アルゴリズムを使用します。特に、Version
カラムは、行が間違った順序で挿入されていても、行を適切に縮小するのに役立ちます。一方、CollapsingMergeTree
は厳密に連続した挿入のみを許可します。
テーブルの作成
クエリパラメータの説明については、クエリの説明 を参照してください。
エンジンパラメータ
パラメータ | 説明 | 種類 |
---|---|---|
sign | 行のタイプを持つカラムの名前:1 は「状態」行、-1 は「キャンセル」行です。 | Int8 |
version | オブジェクトの状態のバージョンを持つカラムの名前です。 | Int* 、UInt* 、Date 、Date32 、DateTime または DateTime64 |
クエリ句
VersionedCollapsingMergeTree
テーブルを作成する場合、MergeTree
テーブルを作成するのと同じ 句 が必要です。
テーブル作成のための非推奨メソッド
新しいプロジェクトでこの方法を使用しないでください。可能であれば、古いプロジェクトを上記で説明した方法に切り替えてください。
sign
と version
を除くすべてのパラメータは MergeTree
での意味と同じです。
-
sign
— 行のタイプを持つカラムの名前:1
は「状態」行、-1
は「キャンセル」行です。カラムデータ型 —
Int8
。 -
version
— オブジェクトの状態のバージョンを持つカラムの名前です。カラムデータ型は
UInt*
である必要があります。
縮小
データ
あるオブジェクトの継続的に変化するデータを保存する必要がある状況を考えてみましょう。オブジェクトごとに1行を持ち、変更があるたびにその行を更新するのが合理的です。しかし、更新操作はデータベース管理システム (DBMS) にとって高コストで遅く、ストレージ内のデータを再書き込みしなければならないため、迅速にデータを書き込む必要がある場合には更新は受け入れられません。そのため、変更を次のように順次オブジェクトに書き込むことができます。
行を書くときに Sign
カラムを使用します。 Sign = 1
の場合、その行はオブジェクトの状態を示します(これを「状態」行と呼びましょう)。 Sign = -1
の場合、同じ属性を持つオブジェクトの状態がキャンセルされたことを示します(これを「キャンセル」行と呼びます)。また、オブジェクトの各状態を特定するために、それぞれ異なる番号を持つ Version
カラムも使用します。
たとえば、ユーザーがあるサイトで訪れたページ数とその滞在時間を計算したいとします。ある時点で、ユーザーアクティビティの状態を持つ次の行を書き込みます。
その後、ユーザーアクティビティの変更を登録し、次の2行で書き込みます。
最初の行はオブジェクト(ユーザー)の以前の状態をキャンセルします。キャンセルされた状態のすべてのフィールドを Sign
を除いてコピーする必要があります。
2行目は現在の状態を含みます。
ユーザーアクティビティの最後の状態のみが必要なため、行
は削除され、オブジェクトの無効な(古い)状態が縮小されます。VersionedCollapsingMergeTree
はデータパーツをマージする際にこれを行います。
各変更について2行が必要な理由については、アルゴリズムを参照してください。
使用上の注意
- データを書き込むプログラムは、オブジェクトの状態を記憶し、それをキャンセルできるようにする必要があります。「キャンセル」文字列には、主キーのフィールドと「状態」文字列のバージョン、および逆の
Sign
のコピーを含める必要があります。これにより、ストレージの初期サイズが増加しますが、データを迅速に書き込むことが可能になります。 - カラム内の長い配列は、書き込み時の負荷によりエンジンの効率を低下させます。データが単純であるほど、効率が良くなります。
SELECT
の結果は、オブジェクトの変更履歴の一貫性に大きく依存します。挿入のためのデータ準備には注意してください。一貫性のないデータでは、セッション深度のような非負メトリックに対して負の値など、予測不可能な結果が得られます。
アルゴリズム
ClickHouseがデータパーツをマージする場合、同じ主キーとバージョンを持ち、異なる Sign
を持つ各行のペアを削除します。行の順序は重要ではありません。
ClickHouseがデータを挿入する場合、行は主キーによって順序付けられます。 Version
カラムが主キーに含まれていない場合、ClickHouseはそれを暗黙的に主キーの最後のフィールドとして追加し、順序付けに使用します。
データの選択
ClickHouseは、同じ主キーを持つすべての行が同じ結果データパーツに存在することや、同じ物理サーバー上にあることを保証しません。これはデータの書き込み時およびその後のデータパーツのマージ時の両方に当てはまります。さらに、ClickHouseは複数のスレッドで SELECT
クエリを処理し、結果の行の順序を予測することはできません。これは、VersionedCollapsingMergeTree
テーブルから完全に「縮小」されたデータを取得する必要がある場合、集約が必要であることを意味します。
縮小を完了するには、GROUP BY
句と記号を考慮した集約関数を持つクエリを書きます。たとえば、量を計算するには count()
の代わりに sum(Sign)
を使用します。何かの合計を計算するには sum(Sign * x)
を使用し、HAVING sum(Sign) > 0
を追加します。
集約 count
、sum
および avg
はこのように計算できます。オブジェクトに少なくとも1つの非縮小状態がある場合、集約 uniq
を計算できます。集約 min
と max
は計算できません。なぜなら、VersionedCollapsingMergeTree
は縮小された状態の値の履歴を保存しないからです。
「縮小」されたデータを集約なしで抽出する必要がある場合(たとえば、行の最新の値が特定の条件と一致するかどうかを確認するため)、FROM
句に FINAL
修飾子を使用できます。このアプローチは非効率的であり、大きなテーブルでは使用すべきではありません。
使用例
例データ:
テーブルの作成:
データの挿入:
私たちは二つの異なるデータパーツを作成するために二つの INSERT
クエリを使用します。データを単一のクエリで挿入すると、ClickHouseは1つのデータパートを作成し、決してマージを行わないでしょう。
データの取得:
ここで何が見えるのか、縮小された部分はどこにありますか?私たちは2つの INSERT
クエリを使用して2つのデータパーツを作成しました。SELECT
クエリは2つのスレッドで実行され、その結果は行のランダムな順序です。データパーツはまだマージされていないため、縮小は行われません。ClickHouseは、私たちが予測できない未知のタイミングでデータパーツをマージします。
だから集約が必要です:
私たちが集約を必要としない場合、そして強制的に縮小を行いたい場合、FROM
句に FINAL
修飾子を使用できます。
これはデータを選択する非常に非効率的な方法です。大きなテーブルには使用しないでください。