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

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* でなければなりません。

崩壊

データ

オブジェクトのために継続的に変化するデータを保存する必要がある状況を考えてみてください。1つの行をオブジェクトのために持ち、変更があるたびにその行を更新するのが合理的です。しかし、更新操作はストレージ内のデータを再書き込みする必要があるため、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は、オブジェクトに少なくとも1つの非崩壊状態がある場合に計算できます。集約関数minmaxは、VersionedCollapsingMergeTreeが崩壊状態の値の履歴を保存しないため、計算できません。

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

使用例

サンプルデータ:

テーブルの作成:

データの挿入:

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

データの取得:

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

これが得られる集約の必要性です:

集約が必要ない場合に崩壊を強制したい場合は、FROM句のためのFINAL修飾子を使用できます。

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