クエリチューニングレシピ
実践的なプレイブック: 症状 → 根本原因 → 実証済みの修正。
プロファイルを開いて問題のあるメトリックを見つけたが、「次に何をすべきか?」という問いに答える必要があるときに使用します。
1 · 迅速な診断ワークフロー
-
実行概要をざっと見る
QueryPeakMemoryUsagePerNode > 80 %またはQuerySpillBytes > 1 GBの場合、メモリとスピルのレシピに直接進みます。 -
最も遅いパイプライン / オペレーターを見つける
⟶ Query Profile UI で Sort by OperatorTotalTime % をクリックします。
最も負荷の高いオペレーターが次に読むべきレシピブロックを教えてくれます(Scan, Join, Aggregate など)。 -
ボトルネックのサブタイプを確認する
各レシピはその_シグネチャ_メトリックパターンから始まります。修正を試みる前にそれらを一致させます。
2 · オペレーター別レシピ
2.1 OLAP / コネクタースキャン [metrics]
Scan Operator 内のさまざまなメトリックをよりよく理解するために、以下の図はこれらのメトリックとストレージ構造との関連を示しています。

ディスクからデータを取得し、述語を適用するために、ストレージエンジンはいくつかの技術を利用します:
- データストレージ: エンコードおよび圧縮されたデータは、さまざまなインデックスと共にセグメントに分けられてディスクに保存されます。
- インデックスフィルタリング: エンジンは、BitmapIndex、BloomfilterIndex、ZonemapIndex、ShortKeyIndex、NGramIndex などのインデックスを活用して不要なデータをスキップします。
- プッシュダウン述語:
a > 1のような単純な述語は、特定の列で評価されるようにプッシュダウンされます。 - 後期実体化: 必要な列とフィルタリングされた行のみがディスクから取得されます。
- 非プッシュダウン述語: プッシュダウンできない述語は評価されます。
- プロジェクション式:
SELECT a + 1のような式が計算されます。
Scan Operator は、IO タスクを実行するための追加のスレッドプールを利用します。したがって、このノードの時間メトリックの関係は以下に示されています。

一般的なパフォーマンスのボトルネック
コールドまたは遅いストレージ – BytesRead、ScanTime、または IOTaskExecTime が支配的で、ディスク I/O が 80–100% に張り付いている場合、コールドデータや性能/容量が不足したストレージに当たっています。ホットデータを NVMe/SSD に移動し、Data Cache を有効化してください。BE の datacache_*(互換の旧 block_cache_*)で容量を設定し、セッション変数 enable_scan_datacache によりスキャン時にキャッシュを利用します。
フィルタープッシュダウンの欠如 – PushdownPredicates が 0 に近いままで ExprFilterRows が高い場合、述語がストレージ層に到達していません。単純な比較として書き直す(%LIKE% や広範な OR チェーンを避ける)か、ゾーンマップ/Bloom インデックスやマテリアライズドビューを追加してプッシュダウンできるようにします。
スレッドプールの枯渇 – 高い IOTaskWaitTime と低い PeakIOTasks は、I/O 並列度の飽和を示します。BE の I/O タスク並列度を上げても改善しません。代わりに、Data Cache を有効化して適切にサイジング(BE datacache_* とセッション enable_scan_datacache)、ホットデータを NVMe/SSD へ移動、上流の並列度を下げる(pipeline_dop を下げる、過剰な同時スキャンを避ける)ことを推奨します。
タブレット間のデータ スキュー – 最大と最小の OperatorTotalTime の間に大きなギャップがある場合、一部のタブレットが他よりも多くの作業をしています。高いカーディナリティのキーで再バケット化するか、バケット数を増やして負荷を分散します。
Rowset/segment の断片化 – RowsetsReadCount/SegmentsReadCount が急増し、長い SegmentInitTime がある場合、多くの小さな rowset が存在します。手動でのコンパクションをトリガーし、小さなロードをバッチ処理してセグメントを事前にマージします。
累積されたソフトデリート – 大きな DeleteFilterRows は、ソフトデリートの使用が多いことを示します。BE コンパクションを実行してソフトデリートをパージします。
2.2 集計 [metrics]

Aggregate Operator は、集計関数、GROUP BY、および DISTINCT の実行を担当します。
集計アルゴリズムの多様な形式
| 形式 | プランナーが選択する条件 | 内部データ構造 | 特徴 / 注意点 |
|---|---|---|---|
| ハッシュ集計 | キーがメモリに収まる; カーディナリティが極端でない | SIMD プロービングを備えたコンパクトなハッシュテーブル | デフォルトパス、適度なキー数に最適 |
| ソート集計 | 入力が GROUP BY キーで既に順序付けされている | 単純な行比較 + 実行状態 | ハッシュテーブルコストがゼロ、プロービングが重いスキューで通常 2-3 倍速い |
| スピル可能な集計 (3.2+) | ハッシュテーブルがメモリ制限を超える | ディスクスピルパーティションを持つハイブリッドハッシュ/マージ | OOM を防ぎ、パイプラインの並行性を維持 |
多段階分散集計
StarRocks では、集計は分散方式で実装されており、クエリパターンとオプティマイザの決定に応じて多段階になることがあります。
┌─────────┐ ┌──────────┐ ┌────────────┐ ┌────────────┐
│ Stage 0 │ local │ Stage 1 │ shard/ │ Stage 2 │ gather/│ Stage 3 │ final
│ Partial │───► │ Update │ hash │ Merge │ shard │ Finalize │ output
└─────────┘ └───────── ─┘ └────────────┘ └────────────┘
| ステージ | 使用される条件 | 実行内容 |
|---|---|---|
| ワンステージ | DISTRIBUTED BY が GROUP BY のサブセットで、パーティションがコロケートされている | 部分集計が即座に最終結果になります。 |
| ツーステージ (ローカル + グローバル) | 一般的な分散 GROUP BY | 各 BE 内の Stage 0 が重複を適応的に圧縮し、Stage 1 が GROUP BY に基づいてデータをシャッフルしてからグローバル集計を実行します。 |
| スリーステージ (ローカル + シャッフル + ファイナル) | 重い DISTINCT と高カーディナリティの GROUP BY | 上記のように Stage 0; Stage 1 が GROUP BY によってシャッフルし、GROUP BY と DISTINCT で集計; Stage 2 が部分状態を GROUP BY としてマージします。 |
| フォーステージ (ローカル + 部分 + 中間 + ファイナル) | 重い DISTINCT と低カーディナリティの GROUP BY | 単一ポイントのボトルネックを避けるために GROUP BY と DISTINCT によってシャッフルする追加のステージを導入します。 |