第4章 パフォーマンス最適化・クエリ・変換 / 想定学習時間:30〜40分 / 最終確認:2026年8月

4-1. Query Profileの読み方とボトルネック診断

🎯 この節の学習目標

1. Query Profileとは:クエリ実行の「レントゲン写真」

クエリが遅いとき、当てずっぽうでウェアハウスを大きくするのは正しいアプローチではありません。Snowflake には、実行済み(または実行中)のクエリが内部でどのように処理されたかを可視化する Query Profile(クエリプロファイル)があります。Snowsight の Monitoring › Query History(クエリ履歴)から対象クエリを選び、「Query Profile」タブを開くと確認できます。

Query Profile には主に次の情報が表示されます。

表示要素内容
演算子ツリー(Operator Tree)TableScan、Filter、Join、Aggregate、Sort、Result などの演算子(オペレーター)がどの順序で実行されたかを示すグラフ。データは下(スキャン)から上(結果)へ流れる
実行時間の割合各演算子がクエリ全体の実行時間の何%を占めたか。最も割合が大きいノードがボトルネックの第一候補になる
統計(Statistics)スキャンしたパーティション数、スキャンバイト数、スピル(あふれ)量、ネットワーク転送量などの詳細な数値
プロファイル概要処理時間の内訳(Processing、Local/Remote Disk I/O、Synchronization など)

📝 試験のポイント

試験では「クエリのパフォーマンス問題を調査するには何を使うか」という形で問われます。個々のクエリの内部動作を演算子レベルで診断するのは Query Profile多数のクエリを横断的に検索・集計するのは QUERY_HISTORY、という役割分担を押さえておきましょう。

2. 見るべきシグナル①:Partitions scanned vs total(プルーニング効率)

統計欄の Partitions scanned(スキャンしたパーティション数)と Partitions total(テーブルの総パーティション数)の比率は、1-6 で学んだパーティションプルーニングがどれだけ効いているかを直接示します。

💡 具体例:プルーニングが効かない書き方

-- 効きにくい:フィルタ列を関数で加工すると、min/max メタデータで絞り込めない
SELECT * FROM sales
WHERE TO_CHAR(order_date, 'YYYY-MM') = '2026-08';

-- 効きやすい:列はそのまま、範囲条件で書く
SELECT * FROM sales
WHERE order_date >= '2026-08-01' AND order_date < '2026-09-01';

Query Profile で TableScan ノードの Partitions scanned / total を見比べれば、書き換えの効果を数値で確認できます。

3. 見るべきシグナル②:スピル(Spillage to local / remote storage)

ソートや大きな集計・結合の途中結果がウェアハウスのメモリに収まらないと、Snowflake はデータを一時的にディスクへ書き出します。これをスピル(spilling)と呼び、統計欄に次の2段階で表示されます。

統計項目意味深刻度
Bytes spilled to local storageメモリに収まらず、ウェアハウスノードのローカルディスクへ書き出した量性能低下のサイン
Bytes spilled to remote storageローカルディスクにも収まらず、リモートのクラウドストレージへ書き出した量より深刻。大幅な性能低下

スピルの根本原因は「処理データ量に対してメモリが不足している」ことなので、対策の基本はウェアハウスサイズを1段階上げる(スケールアップ)ことです。サイズを上げるとノード数とともに総メモリ量が増えるため、スピルが解消してかえって実行時間が短くなり、課金時間の増加を相殺できる場合もあります。あわせて、不要な列を SELECT しない、早い段階で行を絞り込むといったクエリ自体の見直しも有効です。

4. 見るべきシグナル③:Exploding join(結合の爆発)

Join ノードの出力行数が入力行数より大幅に多い場合、結合条件の不足や誤りにより行が掛け算的に膨らんでいる可能性があります。これは Exploding join と呼ばれる典型的なアンチパターンで、結合条件を書き忘れたデカルト積(Cartesian product)が極端な例です。

-- 結合条件が欠けており、行数が m × n に爆発する
SELECT *
FROM orders o
JOIN customers c;  -- ON 句がない!

-- 正しい結合条件を指定する
SELECT *
FROM orders o
JOIN customers c
  ON o.customer_id = c.customer_id;

Query Profile では、Join ノードの入力行数と出力行数を見比べることで発見できます。対策はウェアハウスの増強ではなく、結合条件(結合キー)の見直しです。重複キーによる意図しない多対多結合も同じ症状を引き起こします。

5. 見るべきシグナル④:キュー待ち(Queued time)とTableScan支配

クエリ履歴やプロファイル概要で Queued(キュー待ち)時間が長い場合、クエリ自体は悪くなく、ウェアハウスが過負荷で実行開始を待たされています。同時実行数が多すぎるのが原因なので、対策はサイズアップではなくマルチクラスタウェアハウスによるスケールアウトや、ワークロードを別ウェアハウスに分離することです(詳細は 4-3)。

また、演算子ツリーで TableScan が実行時間の大半を占める場合は、読み取りデータ量そのものが支配的です。フィルタ条件の追加・SELECT する列の削減・クラスタリングの改善など、スキャン量を減らす対策を検討します。

6. 症状→診断→対策の対応表とフロー

ここまでのシグナルを「症状から対策を引く」形に整理します。この対応付けは実務でもそのまま使えます。

症状(Query Profileの所見)診断主な対策
Bytes spilled to local/remote storage が大きいメモリ不足ウェアハウスサイズを上げる(スケールアップ)、クエリの見直し
Queued 時間が長いウェアハウス過負荷(同時実行が多い)マルチクラスタでスケールアウト、ワークロードの別WH分離
Partitions scanned ≒ totalプルーニングが効いていないクラスタリングキーの検討、フィルタの書き方修正
Join の出力行数が異常に多いExploding join(結合条件の不足・重複キー)結合条件の修正、データの重複排除
TableScan が実行時間の大半読み取りデータ量が支配的フィルタ・列の絞り込み、クラスタリング、検索最適化などの検討(4-4)
遅いクエリを発見Snowsight の Query History から対象を特定
Query Profile を開き、演算子ツリーと統計を確認
症状の切り分けどのノードが時間を占めているか/統計に異常はないか
スピルあり→ ウェアハウスサイズアップ
Queued が長い→ マルチクラスタ/別WHに分離
スキャン過多→ プルーニング改善・クラスタリング
結合で行数爆発→ 結合条件を修正

図:症状 → 診断 → 対策の分岐。症状ごとに対策が異なるため、まずプロファイルで診断する

7. QUERY_HISTORYとクエリタグによる調査

個別クエリの深掘りは Query Profile が担当しますが、「昨日遅かったクエリを一覧したい」「アプリ別に実行状況を集計したい」といった横断調査には QUERY_HISTORY を使います。同名で2系統ある点に注意してください。

INFORMATION_SCHEMA.QUERY_HISTORY(テーブル関数)ACCOUNT_USAGE.QUERY_HISTORY(ビュー)
保持期間直近7日間365日間
遅延ほぼリアルタイム最大45分程度の遅延あり
用途直近のクエリの即時調査長期のトレンド分析・監査

💡 具体例:クエリタグで対象を絞り込む

-- セッションにクエリタグを設定しておく(アプリ名やバッチ名など)
ALTER SESSION SET QUERY_TAG = 'nightly_etl';

-- タグを手がかりに、実行時間の長い順に調査する
SELECT query_id, query_text, total_elapsed_time, warehouse_name
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE query_tag = 'nightly_etl'
ORDER BY total_elapsed_time DESC
LIMIT 10;

QUERY_TAG はセッションパラメータで、設定しておくとそのセッションのクエリ履歴にタグが記録されます。バッチ処理やアプリケーションごとにタグを付けておくと、後からの調査・コスト配賦が容易になります。

✅ この節のまとめ

練習問題

問1. Query Profile の統計に「Bytes spilled to remote storage」が大量に記録されていた。最も適切な対策はどれか。

  1. マルチクラスタウェアハウスのクラスタ数上限を増やす
  2. ウェアハウスのサイズを1段階以上大きくする
  3. 結果キャッシュを無効化する
  4. ウェアハウスの自動サスペンド時間を短くする
解答と解説を見る

正解:B

スピルは処理途中のデータがメモリに収まらないことが原因なので、総メモリ量を増やすスケールアップ(サイズ変更)が基本対策です。Aのスケールアウトは同時実行数の問題(キュー待ち)への対策であり、1つのクエリのメモリ不足は解決しません。Cの結果キャッシュはスピルと無関係です。Dはコスト管理の設定であり、性能改善にはつながりません。

問2. あるクエリの Query Profile を確認したところ、WHERE 句で日付を絞り込んでいるにもかかわらず「Partitions scanned」が「Partitions total」とほぼ同じ値だった。この所見が示すことと対策として最も適切なものはどれか。

  1. 結果キャッシュが無効になっているため、USE_CACHED_RESULT を TRUE にする
  2. パーティションプルーニングが効いていないため、クラスタリングキーの設定やフィルタの書き方の見直しを検討する
  3. ウェアハウスが過負荷のため、マルチクラスタ構成に変更する
  4. メモリが不足しているため、ウェアハウスサイズを上げる
解答と解説を見る

正解:B

scanned ≒ total は、フィルタがあるのにほぼ全マイクロパーティションを読んでいる=プルーニングが効いていない状態です。フィルタ列に沿ってデータが並ぶようクラスタリングキーを検討したり、フィルタ列を関数で加工しない書き方に直すことが対策になります。Aの結果キャッシュはスキャン量の問題とは別軸です。Cはキュー待ちが長い場合、Dはスピルが出ている場合の対策です。

問3. ピーク時間帯に多数のユーザーのクエリで「Queued」の時間が長くなっている。クエリ個々の実行時間は短い。最も適切な対策はどれか。

  1. ウェアハウスのサイズを XS から XL に上げる
  2. マルチクラスタウェアハウスを構成してスケールアウトできるようにする
  3. 各クエリに LIMIT 句を追加する
  4. クラスタリングキーをすべてのテーブルに設定する
解答と解説を見る

正解:B

キュー待ちは同時実行数がウェアハウスの処理能力を超えているサインなので、クラスタ数を増やして同時実行を捌くスケールアウト(マルチクラスタ)が適切です。Aのサイズアップは個々の重いクエリを速くする手段で、同時実行の詰まりの解決策としては適切ではありません。C・Dはクエリ単体の工夫であり、キュー待ちの根本原因(同時実行の過多)に対処できません。

問4. QUERY_HISTORY に関する説明として正しいものを2つ選べ。

  1. INFORMATION_SCHEMA.QUERY_HISTORY テーブル関数は直近7日間のクエリを、ほぼリアルタイムに参照できる
  2. ACCOUNT_USAGE.QUERY_HISTORY ビューは365日分を保持するが、データ反映に遅延がある
  3. QUERY_HISTORY を参照すると、各クエリの演算子ツリーがグラフィカルに表示される
  4. QUERY_TAG はテーブルに付与するタグで、クエリ履歴からは参照できない
解答と解説を見る

正解:A、B

2系統の QUERY_HISTORY の違い(7日・即時 vs 365日・遅延あり)はそのとおりです。Cの演算子ツリーのグラフィカル表示は Query Profile の機能であり、QUERY_HISTORY はクエリのメタデータ(実行時間・状態・タグなど)を行として返します。Dは誤りで、QUERY_TAG はセッションに設定するパラメータで、設定した値はクエリ履歴の query_tag 列で参照・絞り込みできます。