Spark SQL 診断
いつ使用するか
ユーザーが以下の任意の組み合わせを提供した場合に使用します:
- Spark UI / History Server / YARN proxy のアドレス;
- Spark SQL とその
EXPLAIN FORMATTED; - Kyuubi / Spark Driver の実行ログ;
/api/v1/...からエクスポートした JSON、または event log。
目標は「いくつかの指標を見る」ことではなく、次の質問に答えることです:
- このクエリのどの層が遅いのか:スキャン、シャッフル、結合、集約、スピル、GC、スケジューリング、コールドスタート;
- データ偏りはあるか、どのステージ / タスク / SQL 断片で発生しているか;
- データの問題か、リソースまたは構成の問題か;
- 次に取るべきアクションは、SQL の修正、構成の変更、データの補完のどれか。
入力の優先順位
| 入力 | できること |
|---|---|
| UI アドレス + Execution ID/送信時間 + SQL | 最も完全、タスクレベルの診断 |
| 実行ログ(app ID と UI アドレスを含む)+ SQL | まずログを解析し、次に UI にアクセス;UI が利用不可の場合はステージレベルにダウングレード |
| EXPLAIN FORMATTED | 計画構造と SQL 断片のマッピングのみ |
| event log / History Server | UI が期限切れになった場合のフォールバック |
ログには通常、以下が含まれています:
application ID: application_xxx
application web UI: http://host:8088/proxy/application_xxx
そのため、ユーザーが別途 UI アドレスを貼り付ける必要はありませんが、実際にアドレスにアクセス可能かどうかを確認する必要があります。
ワークフロー
1. 入力を収集して解析
- ログから app ID、UI アドレス、operation ID、ステージ/ジョブのタイムライン、Final Plan を抽出;
- SQL 原文からテーブル名、結合条件、group by / distinct / window / order by を抽出;
EXPLAIN FORMATTEDから演算子、入出力カラム、結合条件、集約キーを抽出。
2. 実際の実行を特定
- Execution ID を優先;
- それ以外の場合は、SQL テキスト + 送信時間で
/sqlリストとマッチング; /sqlはデフォルトで offset=0 からの少量のレコードのみ返すため、ページネーションまたは ID によるプローブが必要;- 複数の候補がある場合は、最も時間が近く説明が一致するものを選択;それでも一意にならない場合はユーザーに問い合わせる。
3. 実行データを取得
スクリプトを実行します(推奨)。以下の scripts/ はこのスキルディレクトリからの相対パスです。他のディレクトリで実行する場合は、まず cd でスキルディレクトリに移動するか、絶対パスを使用してください。
python scripts/spark_diagnose.py \
--url "http://host:8088/proxy/application_xxx" \
--exec-id 94 \
--sql-file query.sql \
--explain explain.txt \
--log run.log \
--out report.md \
--json report.json
Execution ID が不明な場合:
python scripts/spark_diagnose.py \
--url "http://host:18080/history/application_xxx/" \
--sql-file query.sql \
--exec-time "2026-11-02T08:06:50Z" \
--out report.md
認証パラメータ:
--cookie "name=value; name2=value2"
--header "X-Token: ..."
--user "user:password"
--kerberos # curl --negotiate を使用
--insecure # TLS 検証をスキップ、内部ネットワークの自己署名証明書のみ
読み取り専用の原則:スクリプトは GET のみを呼び出し、SQL を実行せず、クラスタに書き込みを行いません。
4. 指標を解釈
スクリプトは構造化された JSON と Markdown を出力します。重点的に確認する項目:
- ステージタイムライン:ウォールクロック vs
executorRunTimevs タスク起動遅延; - タスク分布:duration、records、shuffle bytes の max/median/p95;
- 真の偏り:1つのタスクの records と duration が同時に中央値を大幅に上回る;
- 偽の長尾:タスクのレコード数は正常だが、
executorDeserializeTimeが非常に高い、通常はコールド executor; - スピル:
memoryBytesSpilled/diskBytesSpilled、および計画ノードのspill size; - 計画ノード:Scan、Exchange、Join、Aggregate の output rows / shuffle bytes / duration;
- 構成:AQE、skewJoin のしきい値、シャッフルパーティション数、ブロードキャストしきい値、動的割り当て。
5. 最終レポートを作成
スクリプトは証拠と候補となる結論を提供します。最終的な返答では、SQL セマンティクスと組み合わせて以下を補足する必要があります:
- 演算子 → SQL 断片のマッピング;
- ホットキーのビジネス上の意味(SQL/テーブル作成ロジックから推測できる場合);
- 提案の優先順位と期待される効果;
- 「外部での検証が必要」と明示的にマークする部分。
レポート構成:
- 結論を先に(影響の大きい順);
- 実行概要とタイムライン;
- 偏り/長尾の証拠テーブル;
- SQL 計画のマッピング;
- 構成とリスク;
- 提案;
- 証拠の出所と限界;
- まだ外部検証が必要な問題。
重要な判断ルール
以下のルールは必ず守り、最も一般的な誤判断を避けてください:
-
偏りは records と duration で見る。shuffle bytes だけでは不十分。 圧縮率の高いホットキー(例:大量の
item_id=1)は数 MB しか占有しないが、レコード数は 60% を占める可能性がある。 -
真の偏りとコールドスタートを区別する。 遅いタスクでレコード数が正常で
executorDeserializeTime / durationが非常に高い場合、それはコールド executor であり、データ偏りではない。 -
部分集約は GROUP BY の偏りを隠す。 計画に
partial_count/partial_sumがある場合、ホットキーはシャッフル前に少数の行に圧縮される。集約の書き方自体が偏りを回避しているかどうかを確認する。 -
count(DISTINCT)は二段階の重複排除。 最初のシャッフルのキーは通常 distinct フィールド(例:(item_id, buyer_id))を含むため、ホットキーは分散される。通常の GROUP BY のように判断しない。 -
AQE skew join はサイズベース。 判断基準は圧縮後のパーティションサイズと
skewedPartitionFactor/skewedPartitionThresholdInBytes。圧縮可能なホットキーはしきい値を下回り、分割されない可能性がある。 -
シャッフルパーティション数を増やしても、単一のホットキーは希釈されない。 単一のキーは常に1つのパーティションに移動する。パーティション数が多いほど他のパーティションは小さくなり、max/median はむしろ大きくなる。
-
ステージのウォールクロックがタスク実行時間を大幅に上回る場合、まずスケジューリングを確認する。
launchTime - submissionTime、動的割り当てminExecutors、YARN キューを確認。 -
結合の出力行数が入力行数を大幅に上回る場合にのみ、爆発を疑う。 出力行数がファクトテーブルの行数と等しい場合、問題は通常分布であり、重複キーではない。
境界
- 実際のデータ内容は見えない。ホットキーの具体的な値はユーザーがデータを確認する必要がある;
- YARN キュー待ち、コンテナ割り当ての遅さの具体的な原因は RM/Kyuubi ログが必要;
- UDF/カスタムコードの問題にはコードと executor ログが必要;
- UI/event log がない場合、通常の driver ログではステージレベルの診断しかできない;
- SQL を実行しない、データを変更しない、cookie/token を表示しない。
参考
references/endpoints.md:REST エンドポイントとページネーション;references/metrics.md:フィールドの違いと単位換算;references/heuristics.md:しきい値、計画、偏り判定。