メインコンテンツまでスキップ
メインコンテンツまでスキップ

VersionedCollapsingMergeTree

このエンジンは:

  • 継続的に変化するオブジェクトの状態を迅速に記録できるようにします。
  • 古いオブジェクトの状態をバックグラウンドで削除します。これにより、ストレージの使用量が大幅に削減されます。

詳細はセクション Collapsing を参照してください。

このエンジンは MergeTree を継承し、データパーツのマージアルゴリズムに行を崩すためのロジックを追加します。VersionedCollapsingMergeTreeCollapsingMergeTree と同じ目的を果たしますが、データを複数スレッドで任意の順序で挿入することを可能にする異なる崩壊アルゴリズムを使用します。特に、Version カラムは、行が間違った順序で挿入されても適切に行を崩すのに役立ちます。それに対して、CollapsingMergeTree は厳密に連続した挿入しか許可しません。

テーブルの作成

クエリパラメータの説明については、クエリの説明 を参照してください。

エンジンパラメータ

パラメータ説明
sign行のタイプを持つカラムの名前: 1 は「状態」行、 -1 は「キャンセル」行です。Int8
versionオブジェクト状態のバージョンを持つカラムの名前。Int*, UInt*, Date, Date32, DateTime または DateTime64

クエリ句

VersionedCollapsingMergeTree テーブルを作成する際には、MergeTree テーブルを作成する際と同じ が必要です。

テーブルを作成するための非推奨メソッド
注記

新しいプロジェクトではこの方法を使用しないでください。可能であれば、古いプロジェクトを上記の方法に切り替えてください。

signversion 以外のすべてのパラメータは、MergeTree と同じ意味を持ちます。

  • sign — 行のタイプを持つカラムの名前: 1 は「状態」行、 -1 は「キャンセル」行です。

    カラムデータ型 - Int8

  • version — オブジェクト状態のバージョンを持つカラムの名前。

    カラムデータ型は UInt* である必要があります。

崩壊

データ

あるオブジェクトの継続的に変化するデータを保存する必要がある状況を考えてみましょう。オブジェクトに対して一行を持ち、変更があるたびにその行を更新するのは合理的です。ただし、更新操作はデータストレージの書き換えが必要なため、DBMS には高コストで遅いです。データを迅速に書き込む必要がある場合、更新は受け入れられませんが、変更を次のようにオブジェクトに順次書き込むことができます。

行を書き込むときに Sign カラムを使用します。Sign = 1 は行がオブジェクトの状態を表すことを意味します(これを「状態」行と呼びます)。Sign = -1 は同じ属性を持つオブジェクトの状態のキャンセルを示します(これを「キャンセル」行と呼びます)。また、Version カラムを使用します。これはオブジェクトの各状態を別の番号で識別する必要があります。

例えば、我々はユーザーがあるサイトで訪れたページ数とその滞在時間を計算したいと思っています。ある時点で、ユーザーアクティビティの状態を示す次の行を書くことができます。

その後、ユーザーアクティビティの変更を登録し、次の2行で書き込みます。

最初の行はオブジェクト(ユーザー)の前の状態をキャンセルします。これはキャンセルされた状態のすべてのフィールドを Sign を除きコピーする必要があります。

2行目は現在の状態を含みます。

ユーザーアクティビティの最後の状態だけが必要なため、以下の行は

削除でき、オブジェクトの無効(古い)状態が崩壊します。VersionedCollapsingMergeTree は、データパーツをマージする際にこれを行います。

なぜ変更ごとに2行が必要なのかを理解するには、アルゴリズム を見てください。

使用に関する注意事項

  1. データを記録するプログラムは、オブジェクトの状態をキャンセルできるように、その状態を記憶している必要があります。「キャンセル」文字列は、プライマリキーのフィールドと「状態」文字列のバージョンおよび逆の Sign を含むコピーを持つ必要があります。これにより初期ストレージサイズが増加しますが、データを書き込むのが迅速になります。
  2. カラムに長大な配列が存在すると書き込み負荷によってエンジンの効率が低下します。データが単純であればあるほど、効率が向上します。
  3. SELECT 結果はオブジェクトの変更履歴の一貫性に大きく依存します。挿入するデータを準備する際は正確に行ってください。不整合なデータによって得られる結果は予測不能であり、セッション深度などの非負メトリクスに対して負の値を得ることがあります。

アルゴリズム

ClickHouse がデータパーツをマージする場合、同じプライマリキーとバージョンを持ち、異なる Sign を持つ各ペアの行を削除します。行の順序は重要ではありません。

ClickHouse がデータを挿入する際、行はプライマリキーで並べ替えられます。Version カラムがプライマリキーに含まれていない場合、ClickHouse はそれを暗黙的に最後のフィールドとしてプライマリキーに追加し、それを使用して並べ替えます。

データの選択

ClickHouse は、同じプライマリキーを持つすべての行が同じ結果データパーツまたは同じ物理サーバーに存在することを保証しません。これはデータの書き込みおよびその後のデータパーツのマージの双方に当てはまります。さらに、ClickHouse は複数のスレッドで SELECT クエリを処理し、結果の行の順序を予測することはできません。したがって、VersionedCollapsingMergeTree テーブルから完全に「崩壊」したデータを得る必要がある場合には集計が必要です。

崩壊を最終化するには、GROUP BY 句と Sign を考慮する集計関数を持つクエリを書きます。例えば、数量を計算するには count() の代わりに sum(Sign) を使用します。何かの合計を計算するには、sum(Sign * x) を使用し、HAVING sum(Sign) > 0 を追加します。

これにより、集計 countsum、および avg をこの方法で計算できます。オブジェクトに少なくとも一つの未崩壊の状態がある場合、集計 uniq を計算できます。集計 min および max は計算できません。なぜなら、VersionedCollapsingMergeTree は崩壊した状態の値の履歴を保存しないからです。

集計なしで「崩壊」したデータを抽出したい場合(例えば、最新の値が特定の条件に一致する行が存在するかを確認するため)、FROM 句に FINAL 修飾子を使用できます。このアプローチは効率が悪く、大規模なテーブルでは使用すべきではありません。

使用例

例のデータ:

テーブルの作成:

データを挿入する:

二つの異なるデータパーツを作成するために二つの INSERT クエリを使用します。データを単一のクエリで挿入すると、ClickHouse は一つのデータパーツを作成し、決してマージを実行しません。

データを取得する:

ここで何が見え、崩壊された部分はどこですか? 我々は二つの INSERT クエリを使用して二つのデータパーツを作成しました。SELECT クエリは二つのスレッドで実行され、結果は行のランダムな順序です。 崩壊はまだ行われていないため、データパーツはまだマージされていません。ClickHouse はデータパーツを未知のタイミングでマージしますが、それを予測することはできません。

これが集計が必要な理由です:

集計が不要で崩壊を強制したい場合、FROM 句に FINAL 修飾子を使用できます。

これは非常に非効率的なデータ選択方法です。大きなテーブルに対しては使用しないでください。