Query Cache
Query Cache は StarRocks の強力な機能で、集計クエリのパフォーマンスを大幅に向上させることができます。ローカル集計の中間結果をメモリに保存することで、以前と同じまたは類似の新しいクエリに対して不要なディスクアクセスや計算を回避できます。Query Cache を使用することで、StarRocks は集計クエリに対して迅速かつ正確な結果を提供し、時間とリソースを節約し、スケーラビリティを向上させます。特に、多くのユーザーが大規模で複雑なデータセットに対して類似のクエリを実行する hc シナリオで有用です。
この機能は v2.5 からサポートされています。
v2.5 では、Query Cache は単一のフラットテーブルに対する集計クエリのみをサポートします。v3.0 以降では、スタースキーマで結合された複数のテーブルに対する集計クエリもサポートします。
適用シナリオ
次のシナリオで Query Cache の使用をお勧めします:
- 単一のフラットテーブルまたはスタースキーマで接続された複数の結合テーブルに対して頻繁に集計クエリを実行する。
- 集計クエリのほとんどが非-GROUP BY 集計クエリおよび低カーディナリティの GROUP BY 集計クエリである。
- データが時間パーティションで追加モードでロードされ、アクセス頻度に基づいてホットデータとコールドデータに分類できる。
Query Cache は次の条件を満たすクエリをサポートします:
-
クエリエンジンが Pipeline である。Pipeline エンジンを有効にするには、セッション変数
enable_pipeline_engineをtrueに設定します。注意
他のクエリエンジンは Query Cache をサポートしていません。
-
クエリがネイティブ OLAP テーブル (v2.5 から) またはクラウドネイティブテーブル (v3.0 から) に対するものである。Query Cache は外部テーブルに対するクエリをサポートしていません。Query Cache は、同期マテリアライズドビューへのアクセスを必要とするプランを持つクエリもサポートします。ただし、非同期マテリアライズドビューへのアクセスを必要とするプランを持つクエリはサポートしていません。
-
クエリが個々のテーブルまたは複数の結合テーブルに対する集計クエリである。
注意
- Query Cache は Broadcast Join と Bucket Shuffle Join をサポートします。
- Query Cache は Join 演算子を含む 2 つのツリー構造をサポートします: Aggregation-Join と Join-Aggregation。Aggregation-Join ツリー構造では Shuffle Join はサポートされて おらず、Join-Aggregation ツリー構造では Hash Join はサポートされていません。
-
クエリに
rand、random、uuid、sleepなどの非決定的な関数が含まれていない。
Query Cache は、次のパーティションポリシーのいずれかを使用するテーブルに対するクエリをサポートします: 非パーティション、マルチカラムパーティション、シングルカラムパーティション。
機能の境界
- Query Cache は Pipeline エンジンの per-tablet 計算に基づいています。per-tablet 計算とは、パイプラインドライバーがタブレット全体を一つずつ処理できることを意味します。クエリのために各 BE が処理する必要のあるタブレットの数が、このクエリを実行するために呼び出されるパイプラインドライバーの数以上である場合、Query Cache は機能します。呼び出されるパイプラインドライバーの数は、実際の並行度 (DOP) を表します。タブレットの数がパイプラインドライバーの数より少ない場合、各パイプラインドライバーは特定のタブレットの一部のみを処理します。この状況では、per-tablet 計算結果を生成できないため、Query Cache は機能しません。
- StarRocks では、集計クエリは少なくとも 4 つのステージで構成されます。最初のステージで AggregateNode によって生成された per-tablet 計算結果は、OlapScanNode と AggregateNode が同じフラグメントからデータを計算する場合にのみキャッ シュされます。他のステージで AggregateNode によって生成された per-tablet 計算結果はキャッシュされません。一部の DISTINCT 集計クエリでは、セッション変数
cbo_cte_reuseがtrueに設定されている場合、データを生成する OlapScanNode と生成されたデータを消費するステージ 1 の AggregateNode が異なるフラグメントからデータを計算し、ExchangeNode によってブリッジされている場合、Query Cache は機能しません。以下の 2 つの例は、CTE 最適化が実行され、Query Cache が機能しないシナリオを示しています:- 出力列が集計関数
avg(distinct)を使用して計算される。 - 出力列が複数の DISTINCT 集計関数によって計算される。
- 出力列が集計関数
- データが集計前にシャッフルされる場合、そのデータに対するクエリは Query Cache によって加速されません。
- テーブルの group-by 列または重複排除列が高カーディナリティの列である場合、そのテーブルに対する集計クエリは大きな結果を生成します。このような場合、クエリは実行時に Query Cache をバイパスします。
- Query Cache は、計算結果を保存するために BE によって提供される少量のメモリを占有します。Query Cache のサイズはデフォルトで 512 MB です。したがって、Query Cache に大きなサイズのデータ項目を保存するのは不適切です。さらに、Query Cache を有効にした後、キャッシュヒット率が低い場合、クエリパフォーマンスが低下します。そのため、タブレットの計算結果のサイズが
query_cache_entry_max_bytesまたはquery_cache_entry_max_rowsパラメータで指定されたしきい値を超える場合、Query Cache はそのクエリに対して機能せず、クエリは Passthrough モードに切り替わります。
動作の仕組み
Query Cache が有効になっている場合、各 BE はクエリのローカル集計を次の 2 つのステージに分割します:
-
Per-tablet 集計
BE は各タブレットを個別に処理します。BE がタブレットの処理を開始するとき、まず Query Cache を調べて、そのタブレットの集計の中間結果が Query Cache にあるかどうかを確認します。もしあれば (キャッシュヒット)、BE は Query Cache から直接中間結果を取得します。もしなければ (キャッシュミス)、BE はディスク上のデータにアクセスし、ローカル集計を行って中間結果を計算します。BE がタブレットの処理を終了すると、そのタブレットの集計の中間結果を Query Cache に格納します。
-
Inter-tablet 集計
BE はクエリに関与するすべてのタブレットから中間結果を収集し、それらを最終結果に統合します。

将来、類似のクエリが発行されると、以前のクエリのキャッシュされた結果を再利用できます。次の図に示すクエリは、3 つのタブレット (Tablet 0 から 2) を含み、最初のタブレット (Tablet 0) の中間結果はすでに Query Cache にあります。この例では、BE はディスク上のデータにアクセスする代わりに 、Query Cache から直接 Tablet 0 の結果を取得できます。Query Cache が完全にウォームアップされている場合、すべてのタブレットの中間結果を含むことができ、したがって BE はディスク上のデータにアクセスする必要がありません。

余分なメモリを解放するために、Query Cache は LRU (Least Recently Used) ベースのエビクションポリシーを採用して、キャッシュエントリを管理します。このエビクションポリシーに従って、Query Cache が占有するメモリ量が事前定義されたサイズ (query_cache_capacity) を超えると、最も最近使用されていないキャッシュエントリが Query Cache から削除されます。
注意
将来的には、StarRocks は Time to Live (TTL) ベースのエビクションポリシーもサポートし、これに基づいてキャッシュエントリを Query Cache から削除できるようになります。
FE は、各クエリが Query Cache を使用して加速する必要があるかどうかを判断し、クエリのセマンティクスに影響を与えない些細なリテラルの詳細を排除するためにクエリを正規化します。
Query Cache の悪いケースによって引き起こされるパフォーマンスのペナルティを防ぐために、BE は実行時に Query Cache をバイパスする適応ポリシーを採用しています。