Databricks Certified Data Engineer Associate 教科書
第6章 トラブルシューティング・監視・最適化(Troubleshooting, Monitoring, and Optimization, 10%)
🎯 この節の学習目標
6-1 で「どのタスクが遅いか」まで絞り込んだら、次はそのタスク(ノートブックやジョブの処理)の内部を Spark UI で覗きます。Spark UI はクラスタの詳細画面や、ジョブ実行の詳細から開けます。Spark の実行は次の3階層で構成され、Spark UI もこの階層に沿ってタブが分かれています。
| 階層 | 単位 | Spark UI で見るもの |
|---|---|---|
| Job | アクション(write、count など)1つに対応する実行単位 | どの処理(アクション)が時間を使っているかの全体像 |
| Stage | Job をシャッフル境界で区切った単位。wide 変換(join / groupBy など)のたびに新しいステージができる | 診断の主戦場。ステージごとの所要時間、shuffle read/write 量、spill 量 |
| Task | Stage 内でパーティション1つを処理する最小単位。並列実行される | タスク間のばらつき(Min/Median/Max)。スキュー検出の決め手 |
ボトルネック診断の基本手順は、「Jobs タブで最も時間のかかっている Job を見つける → その中で最も遅い Stage を開く → Stage 内のタスクメトリクスの分布を読む」という流れです。
ステージの詳細画面には、そのステージの全タスクについての Summary Metrics(要約統計)が表示されます。ここで注目すべきは個々の値ではなく分布、特に Min / Median(中央値)/ Max の関係です。
| メトリクス | 意味 | 異常のサイン |
|---|---|---|
| Duration(タスク実行時間) | 各タスクの処理時間 | Median は短いのに Max だけ突出 → スキューの疑い |
| Shuffle Read / Write | シャッフルで読み書きしたデータ量 | Read の Max が Median の数倍〜数十倍 → 特定パーティションへのデータ集中(スキュー)。全体の量自体が巨大 → シャッフル過多 |
| Spill (Memory / Disk) | メモリに収まらずディスクへ退避したデータ量 | 0 でない値が出ていること自体が黄信号。大きいほど深刻 |
| Input / Output | ステージが読んだ/書いたデータ量 | 想定よりはるかに大きい → フィルタやパーティションプルーニングが効いていない |
📝 試験のポイント
試験では「あるステージのタスクメトリクスがこうなっている。原因は何か/まず確認すべきことは何か」という形式で問われます。判別の軸はただ1つ、「タスク間で均等か、偏っているか」です。偏っている(Max だけ突出)ならスキュー、全タスク一様に重い(shuffle 量や spill が全体的に大きい)ならシャッフル過多・スピル、と読み分けます。
症状:ステージの完了が異常に遅い。進捗が「199/200 タスク完了」のような状態で長時間止まる。
Spark UI での確認箇所:ステージの Summary Metrics で、task duration と shuffle read の Max が Median から大きく乖離していないかを見ます。大半のタスクは数秒〜数分で終わるのに、1〜数個のタスクだけが桁違いに遅い・読み込み量が大きいのが典型パターンです。
原因:結合キーや集計キーの値の分布が偏っており(例:「不明」「NULL」に大量の行が集中)、シャッフル時に特定パーティションへデータが集中している。
対策:
spark.sql.adaptive.enabled、skew join 関連設定)が無効化されていないかを確認するのが第一手です。💡 具体例(公式サンプル問題の型):Min/Median 400MB、Max 5GB
あるジョブの特定ステージで、タスクあたりの shuffle read が Min と Median は約400MBなのに、Max だけが5GBに達しているとします。この「中央値は正常、最大値だけ突出」というパターンはデータスキューの典型的な特徴です。この場合にまず取るべき行動は、AQE が有効で、そのスキュー結合処理(skew join handling)が働く状態になっているかを確認することです。クラスタ全体のメモリ増強やノード追加は、偏り自体を解消しないため根本対策になりません。全パーティション数を増やしても、偏ったキーのデータは結局同じパーティションに集まるため効果は限定的です。
症状:ステージ間の待ち時間が長く、ジョブ全体がネットワークI/Oに支配される。
Spark UI での確認箇所:ステージ一覧で Shuffle Read / Shuffle Write の合計量を確認します。入力データに対してシャッフル量が不釣り合いに大きい、シャッフルを伴うステージが多段に連なっている、といった形で現れます。
原因:join・groupBy・distinct などの wide 変換は、キーごとにデータをノード間で再配置(シャッフル)します。不要な結合の繰り返し、結合前の絞り込み不足、パーティション数の不適合が典型要因です。
対策:
spark.sql.shuffle.partitions の調整 — シャッフル後のパーティション数の既定値が処理規模に合っていないと、パーティションが大きすぎ(スピルの原因)たり細かすぎ(タスク起動オーバーヘッド)たりします。AQE の自動パーティション結合(coalesce)も有効です(3-5 と接続)。症状:タスクは失敗しないが全体的に遅い。実行時間がデータ量の増加以上に伸びる。
Spark UI での確認箇所:ステージのメトリクスにある Spill (Memory) / Spill (Disk) の値を見ます。この値が 0 でなければ、処理中のデータがエグゼキュータのメモリに収まらず、ディスクへの書き出しと読み戻しという高コストなI/Oが発生しています。
原因:1タスクが処理するパーティションが大きすぎる、シャッフルパーティション数が少なすぎる、エグゼキュータあたりのメモリが処理内容に対して不足している、など。
対策:
spark.sql.shuffle.partitions の引き上げ、repartition)3つのボトルネックを1つの診断フローに統合します。分岐の決め手は「タスク間のばらつき」と「spill の有無」です。
図:Spark UI メトリクスによるボトルネック診断フロー
✅ この節のまとめ
spark.sql.shuffle.partitions 調整。スピルにはパーティション数増加とメモリ構成の見直し。問1. あるジョブが遅いため Spark UI を確認すると、特定ステージのタスクあたり shuffle read が Min:400MB、Median:400MB、Max:5GB だった。まず確認・実施すべきこととして最も適切なのはどれか。
正解:B
Median が正常で Max だけ突出しているのはデータスキューの典型パターンであり、第一手は AQE のスキュー結合処理(肥大パーティションの自動分割)が有効かの確認です。Aのノード追加は並列度を上げるだけで、偏ったキーのデータは依然1タスクに集中するため解決しません。Cのメモリ増強は巨大タスクの症状を和らげるだけで根本原因の偏りは残ります。Dのパーティション数増加も、同一キーのデータは同じパーティションに集まるため効果が期待できません。
問2. Spark UI であるステージを確認したところ、ほぼすべてのタスクで「Spill (Disk)」に無視できない値が記録されていた。この現象の説明と対策として最も適切なのはどれか。
正解:B
spill はメモリに収まらないデータのディスク退避で、書き出しと読み戻しの分だけ処理が遅くなります。全タスクで一様に発生している場合は、1タスクあたりのデータ量過大かメモリ不足が原因なので、パーティション数の増加(1タスクの負荷を軽くする)やメモリ最適化インスタンスへの変更が対策です。Aのデータ破損とは無関係です。Cはタスクが失敗しない点では正しく見えますが、性能劣化要因であり放置は不適切です。Dのネットワークはシャッフルの転送に関わりますが、spill はノード内のメモリとディスクの問題です。
問3. 大きなファクトテーブルと小さなマスタテーブル(数MB)の結合を含むジョブで、Spark UI を見るとこの結合のステージで大量の shuffle write が発生していた。シャッフル量を最も直接的に削減できる対策はどれか。
正解:A
片側が十分小さい結合では、小さいテーブルを全エグゼキュータへ配布するブロードキャスト結合により、大きい側のシャッフルを丸ごと省けます。Bの collect はドライバーのメモリを圧迫し OOM の原因になる悪手です(6-4 参照)。Cの列削減は有効な一般則ですが「結合後」では手遅れで、シャッフル前に絞り込む必要があります。Dはリソースの増減であり、シャッフル量そのものは減りません。
問4. Spark UI の階層構造に関する説明として正しいものはどれか。
正解:A
Job(アクション単位)→ Stage(シャッフル境界で分割)→ Task(パーティション単位、並列実行)という階層が正しい理解です。Bは Job と Task の説明が逆です。Cは誤りで、join や groupBy などの wide 変換があるたびに Stage は複数に分かれます。Dも逆で、シャッフルはまさに Stage と Stage の境界で発生するデータ再配置です。