マテリアライズドビューを使用して高速な時系列分析のためのロールアップを構築する
このチュートリアルでは、マテリアライズドビューを使用して、高ボリュームのイベントテーブルから事前集計されたロールアップを維持する方法を示します。 3つのオブジェクトを作成します。生テーブル、ロールアップテーブル、およびロールアップに自動的に書き込むマテリアライズドビューです。
このパターンを使用するタイミング
次の場合にこのパターンを使用します:
- 追加専用のイベントストリーム(クリック、ページビュー、IoT、ログ)がある。
- ほとんどのクエリは、時間範囲の集計(分/時間/日単位)である。
- すべての生行を再スキャンせずに、一貫したサブ秒の読み取りが必要。
生イベントテーブルの作成
ノート
PARTITION BY toYYYYMM(event_time)
は、パーティションを小さく保ち、削除しやすくします。ORDER BY (event_time, user_id)
は、時間制限付きクエリと二次フィルタをサポートします。LowCardinality(String)
は、カテゴリカル次元のメモリを節約します。TTL
は、90日後に生データをクリーンアップします(保持要件に合わせて調整)。
ロールアップ(集計)テーブルの設計
時間粒度に事前集計します。 最も一般的な分析ウィンドウに一致する粒度を選択してください。
集計状態(例:AggregateFunction(sum, ...)
)を保存します。これは部分集計をコンパクトに表現し、後でマージまたは確定できます。
ロールアップのクエリ
状態を読取り時にマージするか、確定することができます:
- 読み取り時にマージ
- -Finalで確定
読み取りが常にロールアップにヒットすることを期待する場合は、1時間粒度で「プレーン」MergeTree
テーブルに確定された数値を書き込む2番目のマテリアライズドビューを作成できます。
状態はより柔軟性を提供し、一方で確定された数値はわずかに単純な読み取りを提供します。
パフォーマンス向上のために主キーのフィールドでフィルタリング
EXPLAIN
コマンドを使用して、インデックスがデータをプルーニングするためにどのように使用されているかを確認できます:
上記のクエリ実行計画では、以下の3種類のインデックスが使用されていることが示されています:
MinMaxインデックス、パーティションインデックス、および主キーインデックスです。
各インデックスは、主キーに指定されたフィールド(bucket_start, country, event_type)
を利用しています。
最良のフィルタリング性能を得るためには、クエリが主キーのフィールドを使用してデータをプルーニングしていることを確認してください。
一般的なバリエーション
- 異なる粒度:日次ロールアップを追加:
次に、2番目のマテリアライズドビュー:
- 圧縮:生テーブルの大きなカラムにコーデックを適用(例:
Codec(ZSTD(3))
)。 - コスト管理:重い保持を生テーブルにプッシュし、長期間のロールアップを保持。
- バックフィル:履歴データをロードする際は、
events_raw
に挿入し、マテリアライズドビューが自動的にロールアップを構築させます。既存の行については、適切であればマテリアライズドビューの作成時にPOPULATE
を使用するか、INSERT SELECT
を使用します。
クリーンアップと保持
- 生のTTLを増やす(例:30/90日)が、ロールアップをより長く保持する(例:1年)。
- TTLを使用して古いパーツを安価なストレージに移動することもできます(階層化が有効な場合)。
トラブルシューティング
- マテリアライズドビューが更新されていない?挿入がevents_raw(ロールアップテーブルではなく)に行われていること、マテリアライズドビューのターゲットが正しいことを確認してください(
TO events_rollup_1h
)。 - クエリが遅い?ロールアップにヒットしていることを確認し(ロールアップテーブルを直接クエリ)、時間フィルタがロールアップ粒度に一致していることを確認してください。
- バックフィルの不一致?
SYSTEM FLUSH LOGS
を使用し、挿入とマージを確認するためにsystem.query_log
/system.parts
をチェックしてください。