テーマ切替
Associate 学習教材 ⑥ トラブルシューティング・最適化
Databricks 公式ドキュメント(日本語版)の内容を、重要用語・概念を漏らさずまとめた自習用教材です。 このファイルだけで学習が完結するよう、各ページの要点を書き起こしています。
参照した公式ページ(すべて実際に読み込み済み):
- Azure Databricks の最適化に関する推奨事項(最適化の全体像) https://learn.microsoft.com/ja-jp/azure/databricks/optimizations/
- データ ファイル レイアウトを最適化する(OPTIMIZE。
delta/optimizeからリダイレクト) https://learn.microsoft.com/ja-jp/azure/databricks/tables/operations/optimize- テーブルに液体クラスタリングを使用する(
delta/clusteringからリダイレクト) https://learn.microsoft.com/ja-jp/azure/databricks/tables/clustering- 多数の小規模な Spark ジョブ https://learn.microsoft.com/ja-jp/azure/databricks/optimizations/spark-ui-guide/small-spark-jobs
- パフォーマンス効率のベスト プラクティス https://learn.microsoft.com/ja-jp/azure/databricks/lakehouse-architecture/performance-efficiency/best-practices
補足として読み込んだ関連ページ(用語を捏造せず正確に補うため):
- Spark UI を使用してコストとパフォーマンスの問題を診断する(Spark UI ガイド目次) https://learn.microsoft.com/ja-jp/azure/databricks/optimizations/spark-ui-guide/
- スキューとスピル(ステージ詳細ページの読み方) https://learn.microsoft.com/ja-jp/azure/databricks/optimizations/spark-ui-guide/long-spark-stage-page
- VACUUM(未使用データファイルの削除) https://learn.microsoft.com/ja-jp/azure/databricks/sql/language-manual/delta-vacuum
1. このセクションの概要
このセクションでは、Databricks(Delta Lake / レイクハウス)でのワークロードの 最適化 と、遅い・失敗するジョブの トラブルシューティング を扱います。ポイントは大きく次の3つです。
- 多くの最適化は自動で行われる — Databricks は「ほとんどのワークロードを最適化する既定値」を構成しており、ユーザーは Databricks を使うだけで恩恵を受けます。ここで説明する動作の多くは Databricks Runtime 10.4 LTS 以降で既定で有効 です。最新の強化を得るには最新の Databricks Runtime を使うのが基本方針です。
- データレイアウトの最適化 — Delta Lake テーブル上のデータファイルの並べ方(ファイルサイズ、クラスタリング、統計)を整えることで、クエリ時に読むデータ量を減らし(データスキッピング)、高速化します。中心となるのが
OPTIMIZE、ZORDER、そして両者を置き換える 液体クラスタリング(Liquid Clustering) です。Unity Catalog マネージドテーブルでは 予測的最適化(predictive optimization) がこれらを自動実行します。 - 診断とチューニング — 遅いジョブは Spark UI(ジョブ/ステージ/タスク、シャッフル、スキュー、スピル)で原因を切り分けます。加えて、クラスターサイズ選定・スケーリング・Photon・キャッシュ・並列化などの パフォーマンス効率のベストプラクティス を適用します。
Delta Lake は Databricks でテーブルを作る際の 既定フォーマット であり、ほとんどの Databricks Runtime の機能は Delta Lake を前提としています。
2. 重要用語集
| 用語(日本語) | English | 説明 |
|---|---|---|
| OPTIMIZE | OPTIMIZE | データファイルを書き換えてレイアウトを改善するコマンド。小さなファイルを圧縮し、必要ならクラスタリングキーでデータをグループ化する。 |
| ファイルコンパクション / ビンパッキング | file compaction / bin-packing | 多数の小さいファイルを、サイズが均等でより大きなファイルに結合する処理。OPTIMIZE の中核。べき等(同じデータに2回実行しても2回目は効果なし)。 |
| small files 問題 | small files problem | 小さいファイルが大量にあると、ファイルごとのオーバーヘッドでクエリが遅くなる問題。コンパクション/自動圧縮で緩和する。 |
| Z オーダー | Z-ORDER (ZORDER BY) | 関連する情報を同じファイル群にまとめる多次元クラスタリング手法。OPTIMIZE ... ZORDER BY (col) で指定。液体クラスタリングがない Delta テーブルで使う。 |
| 液体クラスタリング | Liquid Clustering (CLUSTER BY) | パーティション分割と ZORDER を置き換えるデータレイアウト最適化。既存データを書き換えずにクラスタリングキーを再定義できる。増分的。 |
| 自動液体クラスタリング | Automatic Liquid Clustering (CLUSTER BY AUTO) | クエリ履歴を分析し、Databricks がクラスタリングキーを自動選択する。Unity Catalog マネージドテーブルのみ。 |
| 予測的最適化 | predictive optimization | Unity Catalog マネージドテーブルに対し OPTIMIZE / VACUUM / ANALYZE を自動実行する機能。手動メンテナンスが不要になる。 |
| VACUUM | VACUUM | 参照されなくなった未使用データファイルを削除する。既定の保持期間は 7日間(168時間)。タイムトラベル可能範囲に影響。 |
| データスキッピング | data skipping | 列統計(min/max)を使い、クエリ条件に合致しないファイルの読み取りを省く。既定で先頭 32列 の統計を収集。 |
| 動的ファイルプルーニング | dynamic file pruning (DFP) | クエリ述語に一致しないファイルを含むディレクトリをスキップして高速化。 |
| データスキュー | data skew / skewness | 1つ(少数)のタスクだけが他より極端に時間がかかる状態。クラスター使用率が下がりジョブ全体が長引く。 |
| シャッフル | shuffle | ノード間でデータをネットワーク経由で交換する処理。結合・集計・再パーティションで発生し、コストが高い。 |
| スピル | spill | メモリ不足時にデータをメモリからディスクへ退避する処理。非常にコストが高く、シャッフル中に起きやすい。 |
| Spark UI | Spark UI | ジョブ・ステージ・タスク・シャッフル等を可視化し、コスト/パフォーマンス問題を診断するツール。 |
| ジョブ | job | Spark のアクションに対応する処理単位。複数のステージからなる。 |
| ステージ | stage | シャッフル境界で区切られた処理のまとまり。複数のタスクからなる。 |
| タスク | task | ステージ内で1パーティションを処理する最小実行単位。Executor 上で実行される。 |
| OOM(メモリ不足) | Out Of Memory | メモリ不足によりジョブが遅延・失敗する状態。スピルやスキューの延長で発生しうる。 |
| パーティション | partition / partitioning | データを列値でディレクトリ階層に分割する物理レイアウト。静的で変更が難しく、過剰分割は性能低下を招く。 |
| ディスクキャッシュ | disk cache(旧 Delta キャッシュ) | ワーカーのローカル SSD にリモートデータのコピーを保持し、Parquet 読み取りを高速化。自動管理。 |
| Spark キャッシュ | Spark cache(.persist()) | サブクエリ結果等をメモリ保持する。誤用でメモリを食い潰すため原則回避推奨。 |
| ブロードキャスト結合 | broadcast (hash) join | 小さいテーブルを全 Executor に配布して結合し、シャッフルを避ける手法。AQE が動的に選択しうる。 |
| アダプティブクエリ実行 | Adaptive Query Execution (AQE) | 実行中にプランを再最適化する。結合方式変更・パーティション結合・スキュー処理・空リレーション検出。 |
| Photon | Photon | Spark API 互換の高速ネイティブエンジン。SQL ウェアハウスでは既定で有効。 |
| コストベースオプティマイザー | Cost-Based Optimizer (CBO) | テーブル統計を使い最適なクエリプランを選ぶ。ANALYZE TABLE で統計を収集。 |
| 最適化された書き込み | optimized writes | 書き込み時にファイルサイズを改善し、後続の読み取りを速くする。パーティションテーブルで特に有効。 |
| 自動圧縮 | auto compaction | 書き込み成功後に同期実行され、小さいファイルを自動的に結合する。 |
| 削除ベクトル | deletion vectors | 行の削除を物理書き換えせず記録する機能。行レベルコンカレンシーの前提。液体クラスタリングで既定有効。 |
| 行レベルコンカレンシー | row-level concurrency | 同一テーブルへの同時書き込みの競合を減らす仕組み。 |
| OPTIMIZE FULL | OPTIMIZE FULL | 既存の全レコードを強制的に再クラスタリングする(DBR 16.0 以降)。キー変更時に使用。 |
3. 詳細解説
3-1. 最適化の全体像(何を・なぜ最適化するか)
Databricks は大規模 ETL からアドホックな対話型クエリまで、多様なワークロードを支える最適化を多数提供します。多くは自動的に働き、既定値もほとんどのワークロード向けに設定済みです。ただし場合によっては構成を変えることでさらに性能が上がります。
Databricks Runtime のパフォーマンス強化(DBR 10.4 LTS 以降は既定で有効)
- ディスクキャッシュ(disk cache): コンピュートに接続されたディスク(SSD)にデータを読み込み、Parquet データファイルの 繰り返し読み取り を高速化。
- 動的なファイルの排除(dynamic file pruning / DFP): クエリ述語に一致するデータファイルを含まないディレクトリをスキップして高速化。
- 低シャッフルマージ(low shuffle merge):
MERGEで書き換えるデータファイル数を減らし、マージ後にOPTIMIZEを再実行する必要を減らす。 - アダプティブクエリ実行(AQE): Apache Spark 3.0 で導入。多くの操作の性能を向上(詳細は 3-5 の結合最適化参照)。
パフォーマンス強化に関する推奨事項
- clone: ソースデータセットの deep copy / shallow copy を作成できる。
- コストベースオプティマイザー(CBO): テーブル統計を使いクエリ性能を向上。
- JSON 文字列操作: Spark SQL で文字列を解析せず JSON を操作できる。
- 上位の関数(higher-order functions): 一般的な Spark 演算子がない操作に組み込み最適化を提供。ユーザー定義関数(UDF)より高速。
- 複合データ型(complex types): 配列・構造体・JSON 文字列を操作する組み込み演算子・特別構文。
- 範囲結合の最適化(range join optimization): 手動チューニングが必要。
オプトインの動作
- 既定は「書き込みシリアル化可能(write serializable)」分離。分離レベル(isolation level)をシリアル化可能に上げると 同時実行のスループットが下がりうるが、読み取りシリアル化が必要な場合に使う。
- bloom フィルターインデックス(bloom filter index)は非推奨。代わりに 予測 I/O(predictive I/O) または 液体クラスタリング を使う。
覚え方: 「最適化の大半は自動」「最新 DBR を使う」「Delta Lake が前提」。まず Databricks に任せ、必要に応じて手動調整。
3-2. OPTIMIZE とファイルレイアウト最適化(small files 問題・コンパクション・Z-ORDER)
OPTIMIZE の役割
OPTIMIZE コマンドはデータファイルを 書き換えて レイアウトを改善します(Delta Lake テーブルと Apache Iceberg テーブルの両方が対象)。
- 液体クラスタリング有効テーブル: クラスタリングキーでデータをグループ化 して書き換える。
- パーティション定義済みテーブル: ファイル圧縮とレイアウトはパーティション内 で行われる。
- 液体クラスタリングのない Delta テーブル: 任意で
ZORDER BY句を付け、書き換え時のクラスタリングを改善できる。
Databricks の推奨は「パーティション・
ZORDER・その他ではなく 液体クラスタリング を使う」こと。
small files 問題とビンパッキング(bin-packing)
小さいファイルが大量にあると、ファイルごとの処理オーバーヘッドで読み取りが遅くなります(small files 問題)。OPTIMIZE はこれらを結合する ビンパッキング(bin-packing / コンパクション) を行います。重要な性質:
- べき等(idempotent): 同じデータセットに2回実行しても、2回目は効果がない。
- ビンパッキングは ストレージ上のサイズが均等 になるようファイルを生成する(ファイルあたりのタプル数で厳密に調整されるわけではないが、両者はしばしば相関する)。
読み取りへの影響(スナップショット分離)
Delta テーブルの読み取りは スナップショット分離(snapshot isolation) を使うため、OPTIMIZE が不要ファイルを削除しても中断されません。OPTIMIZE はデータの中身を変えないので、実行前後で読み取り結果は同じです。ストリーミングソースのテーブルに実行しても、現在・将来のストリームに影響しません。
戻り値・統計
OPTIMIZE は削除・追加したファイルのファイル統計(min / max / 合計など)を返します。最適化統計には Z オーダー統計、バッチ数、最適化されたパーティション も含まれます。
自動圧縮(auto compaction)
OPTIMIZE を手動で実行する代わりに、自動圧縮 で小さいファイルを自動結合できます。書き込み成功後に、その書き込みを実行したクラスター上で 同期的 に実行され、以前に圧縮されていないファイルのみ を圧縮します。関連して 最適化された書き込み(optimized writes) は、書き込み時にファイルサイズを改善し、パーティションテーブルで特に有効です。
実行頻度と実行環境
- 頻度のトレードオフ: 高頻度に実行するほどクエリ性能は上がるが、リソース使用が増えコスト増。低頻度ほど低コストだが性能は下がる。Databricks は まず毎日実行し、その後コストと性能のバランスで頻度調整 を推奨。
- 予測的最適化を有効にすれば
OPTIMIZEはコスト効率の良いタイミングで自動実行される(推奨)。 - 推奨インスタンス:
OPTIMIZEは大量の Parquet デコード/エンコードを行う CPU 集中型 操作のため、コンピューティング最適化(compute-optimized) インスタンスが推奨。接続 SSD の恩恵も受ける。
3-3. 液体クラスタリング(Liquid Clustering)と予測的最適化(predictive optimization)
液体クラスタリングとは
パーティション分割と ZORDER に代わる データレイアウト最適化手法。クラスタリングキーに基づきデータを自動整理し、テーブル管理を簡素化しクエリ性能を最適化します。従来のパーティション分割と違い、既存データを書き換えずにキーを再定義でき、分析ニーズの変化に合わせてレイアウトを進化させられます。ストリーミングテーブルと具体化ビューにも適用可能。
- 一般提供: Delta Lake は DBR 15.4 LTS 以降、Apache Iceberg は DBR 16.4 LTS 以降(パブリックプレビュー扱いから GA)。
- クラスタリングは パーティション分割・
ZORDERと併用不可。
いつ使うか(利点が大きいテーブルの特性)
- カーディナリティの高い列 でフィルターするクエリ。
- データの偏り(skew)が大きい テーブル。
- 急速に成長し、メンテナンス・チューニングを要するテーブル。
- 同時書き込み 要件があるテーブル。
- アクセスパターンが多様/時間とともに変化するテーブル。
- 典型的なパーティションキーだと パーティション数が多すぎ/少なすぎ になるテーブル。
Databricks は、ストリーミングテーブル・具体化ビューを含む すべての新規テーブルに液体クラスタリングを推奨。
有効化のしかた(要点)
- 新規作成:
CREATE TABLE ... CLUSTER BY (col)。既存データから作る場合はCLUSTER BYをSELECTの前(テーブル名の後)に置く。 - 既存の非パーティションテーブル:
ALTER TABLE <t> CLUSTER BY (<cols>)。ただし 既定では過去に書き込んだデータには適用されない。強制再クラスタリングはOPTIMIZE FULL。 - DataFrame API(DBR 14.3 LTS 以降):
df.write.clusterBy("col").saveAsTable(...)など。ただし DataFrame API でキー指定できるのは テーブル作成時かoverwriteモード時のみ。appendではキー変更不可。データ追加中に変えたい場合は SQLALTER TABLEで別途変更する。 - パーティションテーブルからの変換(DBR 18.1 以降):
ALTER TABLE <t> REPLACE PARTITIONED BY WITH CLUSTER BY [(cols) | AUTO]。ダウンタイム最小で外部・マネージド両対応。変換後は DBR 13.3 LTS 以降で読み取り可。 - クラスタリングキー削除:
ALTER TABLE <t> CLUSTER BY NONE(既存のクラスタ済みデータは書き換えないが、以降のOPTIMIZEでキーが使われなくなる)。
プロトコル注意: 液体クラスタリング有効の Delta テーブルは ライターバージョン7・リーダーバージョン3 を使用。非対応クライアントは読めず、プロトコルのダウングレード不可。
クラスタリングキーの選び方
- クエリフィルターで最もよく使う列 を選ぶ。
- 最大4つ まで指定可。順序は任意。小さいテーブル(10 TB 未満)でキーが多すぎると、単一列フィルター時に性能が落ちうる(例: 4キー < 2キー)。テーブルが大きくなると単一列クエリでの差はごくわずか。
- 高相関の2列はどちらか一方だけをキーにする。
- キーは統計が収集された列であること(既定で先頭32列の統計を収集。
data-skippingの統計列指定で調整可)。 - サポート型: Date, Timestamp, TimestampNTZ(14.3 LTS 以降), String, Integer/Long/Short/Byte, Float/Double/Decimal。構造体フィールド(
CLUSTER BY (struct_col.field)、ネストも可)も可。複合型(Struct/Map/Array)そのものや配列/マップ要素はキーにできない。 - 移行の指針: Hive パーティション列 → クラスタリングキーに。
ZORDER BY列 → キーに。生成列(タイムスタンプから日付など)は使わず 元の列 をキーにする。
自動液体クラスタリング(CLUSTER BY AUTO)
DBR 15.4 LTS 以降、Unity Catalog マネージド Delta テーブルで利用可(Iceberg v3 は DBR 18.0 以降)。CLUSTER BY AUTO で Databricks がキーを インテリジェントに自動選択。
- 仕組み: 履歴クエリワークロードを分析 → 最適候補列を識別。パターンやデータ分布の変化に 適応 して新しいキーを選ぶ。コスト対応(データスキップによる削減 > クラスタリングコスト のときだけキーを変更)。非同期実行で、キー選択とクラスタリング操作は 予測的最適化 が担う。
- キーを選ばない場合: テーブルが小さすぎる/既に有効なクラスタリングがある/頻繁なクエリがない/DBR 15.4 LTS 未満、など。
- 有効化:
CLUSTER BY AUTO。確認はDESCRIBE TABLE/SHOW TBLPROPERTIES(clusterByAuto=true、現在のキーはclusteringColumns)。
クラスタリングのトリガーとサイズしきい値
- 予測的最適化が有効なら
OPTIMIZEは自動実行される(このときスケジュールOPTIMIZEジョブは 無効化推奨)。 - 手動なら定期的に
OPTIMIZE table_name;。液体クラスタリングは 増分的 で、必要なデータのみ書き換えるので通常は高速。更新・挿入が多いテーブルは 1〜2時間ごと のOPTIMIZEを推奨。 - 書き込み時クラスタリング は、トランザクション内データがサイズしきい値を超えた時のみ発動。しきい値はキー数で変わり、Unity Catalog マネージドテーブルの方が低い:
| クラスタリング列数 | UC マネージド | その他 Delta |
|---|---|---|
| 1 | 64 MB | 256 MB |
| 2 | 256 MB | 1 GB |
| 3 | 512 MB | 2 GB |
| 4 | 1 GB | 4 GB |
すべての操作でクラスタリングされるわけではないため、OPTIMIZE を頻繁に実行するのが推奨。書き込み時クラスタリング対応操作: INSERT INTO、CTAS/RTAS、Parquet からの COPY INTO、spark.write.mode("append")。
再クラスタリングの強制(OPTIMIZE FULL)
- DBR 16.0 / 16.4 LTS 以降:
OPTIMIZE table_name FULL;で 全レコードを強制再クラスタリング。キーで未クラスタの大きいテーブルでは数時間かかることがある。 - クラスタリングを初めて有効化した時/キーを変更した時 に実行する。キー変更がなければ
OPTIMIZE FULLは通常のOPTIMIZE(増分)と同じ動作。 - 部分再クラスタ(DBR 18.1 以降):
OPTIMIZE table FULL WHERE <predicate>。
予測的最適化(predictive optimization)
- Unity Catalog がマネージドテーブルの全読み書き・クエリパターンを把握し、実際の使われ方に応じて自動でメンテナンス(レイアウト最適化=
OPTIMIZE、古いファイルのクリーンアップ=VACUUM、クラスタリング更新、統計収集=ANALYZE)を実行。手動管理が不要になり、通常は性能が大幅向上。 - Databricks は すべての Unity Catalog マネージドテーブルで有効化を推奨(アカウント/カタログ/スキーマ単位で有効化可)。
3-4. Spark UI での診断(ステージ/タスク/シャッフル/スキュー/スピルの読み方)
Spark UI の開き方と全体像
- クラスターのページ → [Spark UI] をクリック。
- 前提概念: ドライバー(driver)/ワーカー(worker)/Executor/ステージ(stage)/タスク(task)。
- ジョブ(job): Spark アクションに対応。複数ステージからなる。
- ステージ(stage): シャッフル境界で区切られる。複数タスクからなる。
- タスク(task): 1パーティションを処理する最小単位。Executor 上で並列実行。
診断の手順(Spark UI ガイドの流れ)
- ジョブタイムライン(jobs timeline) で主要な問題を特定する。
- 最長のステージ(longest stage) を見る。
- スキュー(skew)またはスピル(spill)を探す。
- 最長ステージが 入出力(I/O)依存 かを判断する。
- それ以外の低速要因を探す。
スピル(spill)の読み方
- 最初に確認すべきはスピルの有無。ステージ詳細ページ上部の統計に スピル情報 が出る場合がある。
- スピルとは: Spark が メモリ不足 のとき、データをメモリからディスクへ移す処理。非常にコストが高い。データシャッフル中に最も起きやすい。
- スピル統計が表示されなければ、そのステージにスピルは無い。表示された場合はシャッフル起因のスピルへの対処が必要。
- 指標名: Spill (Memory) / Spill (Disk)(メモリ上・ディスク上に退避された量)。
スキュー(skew)の読み方
- スキュー = 1つ/少数のタスクが他より極端に時間がかかる 状態。クラスター使用率が下がりジョブが長引く。
- 見方: ステージ詳細ページの [Summary Metrics(概要メトリック)] までスクロールし、タスク所要時間の 最小 / 25% / 中央値(median) / 75% / 最大(Max) を見る。
- 健全なステージ: 75 パーセンタイルと最大がほぼ同じ。
- 最大が 75 パーセンタイルより 50% 以上長い場合、スキューの可能性が高い。
- スキューが見つかればスキュー修復(後述)を行う。スキューもスピルも無ければ、ジョブページに戻り高 I/O ページ等を確認する。
シャッフル(shuffle)
- シャッフル はノード間でネットワーク経由にデータを交換する処理(結合・集計・再パーティションで発生)。分散処理のオーバーヘッド の主因であり、スピルの温床。過剰なシャッフルは監視対象(Spark イベントログで検出可能)。
スキュー・スピルへの主な対処(本セクションおよびベストプラクティスから)
- AQE(アダプティブクエリ実行)を有効に保つ — 実行中に スキューを動的に処理、パーティション結合、結合方式の動的変更などを行う。
- 大きいテーブルと小さいテーブルの結合では ブロードキャスト結合(broadcast hash join) でシャッフルを回避(AQE が動的選択しうる)。
- レイアウト側の改善: 液体クラスタリング/
OPTIMIZEによるデータスキップで読み取り・シャッフル量を削減。
スキュー・スピル・過剰シャッフル・OOM は、Spark イベントログの分析(クラスターログ配信を構成)で履歴的に特定できる。実行時間の長いステージも同様。
3-5. パフォーマンス効率のベストプラクティス(スケーリング・クラスターサイズ・OOM 対処など)
1. 垂直/水平スケーリングと線形スケーラビリティ
- 垂直スケーリング(vertical scaling): 1台のマシンに CPU/メモリ/GPU を増減。より大きいマシンが無い/価格が跳ね上がる、という限界がある。
- 水平スケーリング(horizontal scaling): クラスター(複数マシン)にノードを増減。垂直の限界時の解。ノード数に技術的上限はない(Spark が負荷分散を担う)が、多いほど管理が複雑化。
- 線形スケーラビリティ(linear scalability): リソース追加とスループットが線形になる関係。並列タスクが独立している場合のみ成立。依存があるとノード間データ交換(ネットワーク転送=シャッフル)が発生しオーバーヘッドになる。小さいデータは分散させると単一ノードより遅くなることもある。
2. サーバーレスアーキテクチャを使う
- サーバーレスコンピュート(serverless compute): フルマネージド、従量課金、クラスター起動待ち時間がほぼゼロ、クエリコンカレンシー向上。クラウドの低レベル管理が不要。
- 種類: サーバーレス SQL ウェアハウス、サーバーレスジョブ(オートスケーリングと Photon が自動有効)、ノートブック用サーバーレスコンピュート。
- Model Serving: AI モデルを REST API で提供。需要に応じ自動スケール。サーバーレスコンピュートを利用。
3. パフォーマンスのためにワークロードを設計する
- データインジェスト/アクセスパターンを理解する: 大きいファイルはスキャンクエリに有利、小さいファイルは検索(特定行取得)に有利。DML はデータがクラスタ化・分離可能なときに最も高速。自然な時間順序を維持し、フィルターを多く適用できるよう設計。
- 並列計算を使う: SQL は自動的に全ノードで並列化。Lakeflow パイプライン、構造化ストリーミング、Spark 上の pandas API、MLlib、分散学習(DeepSpeed / TorchDistributor)などを活用。
- 実行チェーン全体を分析: BI → コネクタ → SQL エンジン、のように連鎖全体を調整する。
- より大きいクラスターを優先する: ワークロードが線形にスケールするなら、大きいクラスターでも総コストは変わらず「速いだけ」(例: 2ワーカー×1時間 と 4ワーカー×30分 は同コスト)。SLA が柔軟でコスト最優先なら オートスケーリング(自動スケーリング)クラスター が最安になりやすいが最速とは限らない。※サーバーレスでは大きいクラスターを優先する必要はない(自動管理)。
- 予測的最適化を使う(3-3 参照)。
- Unity Catalog のマネージドテーブルを使う: レイアウトや予測的最適化を Databricks が自動管理。外部テーブルは保守・最適化を自分で行う必要がある。
- ネイティブ Spark 機能を使う: ネイティブ関数があるなら Python/Scala UDF を避ける(Python↔Spark 間のシリアル化でクエリが大幅に遅くなる)。どうしても UDF が必要なら Pandas UDF(Apache Arrow で効率的にデータ移動)を使う。
- Photon を使う: Spark API 互換の高速エンジン。コード変更・ロックインなしで有効化でき、SQL ウェアハウスでは既定有効。
- ハードウェアとワークロード種別を理解する: VM ファミリー(RAM/コア、プロセッサ世代、ネットワーク帯域、ローカル SSD、スポット市場)を理解して選ぶ。※サーバーレスでは不要。
キャッシュを使う(4種類)
| キャッシュ | 概要 | 注意 |
|---|---|---|
| ディスクキャッシュ | ローカル SSD にリモートデータのコピー。作成・削除を自動検出し更新。SSD ワーカーを選ぶのが最も簡単で推奨 | サブクエリ結果の保存には使えない |
| Spark キャッシュ | .persist()/.unpersist()。CSV/JSON/ORC 等も保存可 | 原則回避(誤用でメモリを食い潰し遅くなる) |
| クエリ結果キャッシュ | SQL ウェアハウス単位。決定論的クエリ・Delta が未変更なら結果を直接返す | = NOW() 等の非決定論的述語は不可 |
| Databricks SQL UI キャッシュ | SQL UI の結果をユーザー単位でキャッシュ | — |
圧縮(コンパクション)を使う: OPTIMIZE で小さいファイルを結合。自動圧縮(書き込み後に小さいファイルを結合)と 最適化された書き込み(書き込み時にファイルサイズ改善、パーティションテーブルで有効)を活用。
データスキッピングを使う: 書き込み時に統計(既定で先頭32列の min/max)を自動収集し、クエリ時に不要ファイルをスキップ。データスキップには液体クラスタリングを推奨。
過剰なパーティション分割を避ける: パーティションは静的でファイルシステム階層になり、変更しづらい。過剰分割(小さすぎるファイルでの多すぎるパーティション)は性能低下を招く。目安: 1 TB 未満のテーブルはパーティション分割しない。各パーティションが 1 GB 以上見込めるなら列でパーティション分割。新規テーブルはパーティションより液体クラスタリングを使う。
結合のパフォーマンスを最適化する
- 範囲結合の最適化(range join optimization): 区間の点/重なり条件の結合。桁違いに速くなりうるが慎重な手動チューニングが必要。
- AQE(アダプティブクエリ実行) の4つの主機能: ①ソートマージ結合を ブロードキャストハッシュ結合 に動的変更、②シャッフル交換後にパーティションを動的結合、③ソートマージ/シャッフルハッシュ結合の 偏り(skew)を動的処理、④空リレーションの動的検出・伝播。AQE は有効のまま推奨。
ANALYZE TABLE で統計を収集する: CBO が最適プラン・結合種別・結合順序を決めるのに使う。予測的最適化は Unity Catalog マネージドテーブルで ANALYZE(パブリックプレビュー)を自動実行。
4. 開発時にパフォーマンステストを行う
- 本番同等データ(ボリューム・ファイルレイアウト・スキューが本番相当)でテスト。
- リソースの事前ウォーミング(pre-warming)を検討: クラスターでの最初のクエリは常に後続より遅い。Databricks プール(アイドルインスタンス群)でクラスター起動/オートスケール時間を短縮。キャッシュの事前ウォーミング(特定クエリでキャッシュを初期化)で最初の数クエリを高速化。
- ボトルネックを特定: 負荷増で全体性能を落とす箇所を設計時に特定し、高負荷でテスト。
5. パフォーマンスの監視
- システムテーブル:
system.compute(クラスター使用率)、system.workflow(ジョブ性能)、system.query(SQL ウェアハウスのクエリメトリック)。 - Spark 監視: クラスターログ配信で Spark イベントログ を保持し、実行時間の長いステージ・データスキュー・過剰シャッフル・メモリ不足(OOM) を特定。
- ジョブ/パイプライン監視、クエリ性能監視(クエリプロファイル(query profile) で各タスクの所要時間・処理行数・使用メモリを可視化し実行プランを分析)。
- SQL ウェアハウス監視、ストリーミング監視(Spark UI の構造化ストリーミング監視、または StreamingQueryListener)。
OOM(メモリ不足)への対処の考え方
ドキュメント横断でのポイント:
- OOM/スピルは メモリ不足 が根本。Spark イベントログ・Spark UI で スキュー・過剰シャッフル・スピル を確認して原因を切り分ける。
- 対策例: AQE を有効化(スキュー・パーティション結合を動的最適化)、より大きい/垂直スケールしたクラスター(またはサーバーレスの自動管理)、Spark キャッシュの誤用を避ける(メモリ枯渇要因)、データスキップ/液体クラスタリング で読み取り・シャッフル量を削減、過剰パーティション分割の回避。
多数の小規模な Spark ジョブ(small Spark jobs)
- 症状: 小さいジョブが大量(比較的小さいデータ <10 GB に多数の操作)。各操作は数秒でも積み重なり、操作ごとのオーバーヘッド が支配的になる。
- 最善策は 複数操作を並列実行 すること。Lakeflow パイプラインは自動でこれを行う。
- 具体的オプション:
- 操作を複数ノートブックに分割し、複数タスクジョブ(multiple task jobs) で同一クラスター上で並列実行。
- すべて SQL なら SQL ウェアハウス を使う(この種のワークロード向けに設計され、多数クエリの並列実行でよくスケールする)。
- ノートブックを パラメータ化(widgets) し、for each タスク で複数回並列実行。コンカレンシー(concurrency) で並列度を設定。サーバーレスコンピュート で好適に動作。
4. 構文・コード例
OPTIMIZE(コンパクション)
sql
-- テーブル全体を最適化(コンパクション)
OPTIMIZE table_name;
-- パーティション述語で対象を絞る
OPTIMIZE table_name WHERE date >= '2022-11-18';python
# Python(Delta Lake 固有の DeltaTable API)
from delta.tables import *
deltaTable = DeltaTable.forName(spark, "table_name")
deltaTable.optimize().executeCompaction()
deltaTable.optimize().where("date='2021-11-18'").executeCompaction()ZORDER BY(液体クラスタリングのない Delta テーブル)
sql
-- 書き換え時に指定列でデータクラスタリングを改善
OPTIMIZE table_name ZORDER BY (col_a, col_b);液体クラスタリング(CLUSTER BY)
sql
-- 空テーブルを作成
CREATE TABLE table1 (col0 INT, col1 STRING) CLUSTER BY (col0);
-- 既存データから作成(CLUSTER BY は SELECT の前)
CREATE TABLE table2 CLUSTER BY (col0) AS SELECT * FROM table1;
-- 構造(クラスタリング構成含む)をコピー
CREATE TABLE table3 LIKE table1;
-- 既存の非パーティションテーブルで有効化
ALTER TABLE my_table CLUSTER BY (col1, col2);
-- クラスタリングキーを変更
ALTER TABLE my_table CLUSTER BY (new_col1, new_col2);
-- クラスタリングを無効化
ALTER TABLE my_table CLUSTER BY NONE;python
# DataFrame API(DBR 14.3 LTS 以降)
df = spark.read.table("table1")
df.write.clusterBy("col0").saveAsTable("table2")自動液体クラスタリング(CLUSTER BY AUTO)
sql
-- 作成時に自動選択を有効化
CREATE OR REPLACE TABLE table1 (c1 INT, c2 STRING) CLUSTER BY AUTO;
-- 既存テーブルで有効化
ALTER TABLE table1 CLUSTER BY AUTO;
-- 初期キーのヒントを与えてから自動に切り替え
ALTER TABLE table1 CLUSTER BY (c1, c2);
ALTER TABLE table1 CLUSTER BY AUTO;
-- 自動をオフ
ALTER TABLE table1 CLUSTER BY NONE;再クラスタリングの強制(OPTIMIZE FULL)
sql
-- 全レコードを強制再クラスタリング(DBR 16.0/16.4 LTS 以降)
OPTIMIZE table_name FULL;
-- 述語でサブセットだけ強制再クラスタリング(DBR 18.1 以降)
OPTIMIZE events FULL WHERE event_date >= '2025-01-01';パーティションテーブル → 液体クラスタリング変換(DBR 18.1 以降)
sql
-- 元のパーティションと異なる列にクラスタ化
ALTER TABLE t1 REPLACE PARTITIONED BY WITH CLUSTER BY (day, id);
OPTIMIZE t1; -- メリットを得るには OPTIMIZE が必要
-- 自動液体クラスタリングで現在のパーティション列から開始
ALTER TABLE t2 REPLACE PARTITIONED BY WITH CLUSTER BY AUTO;
-- 現在のパーティション列をそのままキーに
ALTER TABLE t3 REPLACE PARTITIONED BY WITH CLUSTER BY;クラスタリングキーの確認
sql
DESCRIBE TABLE table_name;
DESCRIBE DETAIL table_name;
DESCRIBE EXTENDED table_name; -- 変換確認
DESCRIBE HISTORY table_name; -- REORG/UPGRADE PROTOCOL 等の履歴確認VACUUM(未使用ファイルの削除)
sql
-- 既定の保持期間(7日)で未使用ファイルを削除
VACUUM table_name;
-- 削除対象ファイル(最大1000件)を一覧表示するだけ(実削除しない)
VACUUM table_name DRY RUN;
-- FULL/LITE モード(DBR 16.1 以降)。既定は FULL
VACUUM table_name FULL;
VACUUM table_name LITE;
-- 保持期間(=タイムトラベル可能範囲)を延ばす(例: 30日)
ALTER TABLE table_name SET TBLPROPERTIES ('delta.deletedFileRetentionDuration' = '30 days');VACUUM の要点
- 参照されなくなった未使用データファイルを削除する。
_(アンダースコア)で始まるディレクトリ(_delta_logを含む)はスキップ。 - 削除判定は「ストレージ上の変更日時」ではなく「Delta トランザクションログ上で論理削除された時刻+保持期間」に従う。既定しきい値は 7日間。
- 実行後、指定保持期間より古いバージョンにはタイムトラベルできなくなる。
- 保持期間は
delta.deletedFileRetentionDuration(既定7日)で決まる。 - 警告: 保持期間は 7日以上 を強く推奨。実行時間の長いジョブが未コミットのファイルを書き込んでいると、短すぎる保持期間だと 完了前に削除 されうる。危険な
VACUUMは安全性チェックで防止される(spark.databricks.delta.retentionDurationCheck.enabled=falseで無効化できるが非推奨)。 - 予測的最適化が有効なら
VACUUMは自動実行 され、手動実行はほぼ不要。 FULL/LITEモードは DBR 16.1 以降。既定はFULL。LITEはトランザクションログのみで削除対象を特定し、テーブルディレクトリの全ファイル一覧を避けるため高速・低コスト(頻繁にVACUUMが必要な大規模テーブル向け)。ただしログに参照されていないファイル(中止されたトランザクションが作ったファイル等)は削除されない。要件は「トランザクションログ保持しきい値(既定 30日)内に、少なくとも 1 回の成功したVACUUM実行があること」で、満たさない場合はFULLでの実行を促すエラーになる。- Iceberg テーブルの保持期間は 7日で固定。
ANALYZE TABLE(統計収集 / CBO 用)
sql
ANALYZE TABLE table_name COMPUTE STATISTICS;5. 試験で問われるポイント
- OPTIMIZE の目的: 小さいファイルを結合(ビンパッキング / コンパクション)してレイアウトを改善する。べき等。データ内容は変えず、読み取り結果は前後で不変。
- small files 問題: 小さいファイル大量 → オーバーヘッドで遅い。→
OPTIMIZE/自動圧縮/最適化された書き込みで対処。 - Z-ORDER vs 液体クラスタリング:
ZORDER BYは液体クラスタリングのない Delta テーブルの手法。新規テーブルは液体クラスタリング(CLUSTER BY)が推奨。液体クラスタリングは 既存データを書き換えずキーを再定義でき、増分的。両者・パーティションとは併用不可。 - 液体クラスタリングのキー: 最大4つ、統計収集列(既定先頭32列)から選ぶ。クエリフィルターで最頻の列。
- CLUSTER BY AUTO: Unity Catalog マネージドテーブルのみ。履歴クエリからキーを自動選択、コスト対応。
- 予測的最適化: Unity Catalog マネージドテーブルで
OPTIMIZE/VACUUM/ANALYZEを自動実行。手動メンテ不要。有効化推奨。 - VACUUM: 未使用ファイル削除。既定保持7日。短くしすぎると未コミットファイル削除やタイムトラベル不能のリスク。
delta.deletedFileRetentionDurationで調整。DRY RUNで確認。 - スキュー: Spark UI の Summary Metrics で 最大が 75 パーセンタイルより 50% 以上長い ならスキューの疑い。→ AQE が動的処理。
- スピル: メモリ不足でメモリ→ディスク退避。シャッフル中に多発。ステージ上部の統計(Spill Memory/Disk)で確認。
- シャッフル: ノード間データ交換。結合・集計で発生しコスト高。線形スケーラビリティを崩す要因。
- ジョブ/ステージ/タスク: ジョブ⊃ステージ(シャッフル境界で区切り)⊃タスク(1パーティション=1タスク、Executor で並列)。
- キャッシュの使い分け: ディスクキャッシュ(SSD ワーカー)を優先、Spark キャッシュ(.persist)は原則回避。
- 過剰パーティション分割: 1 TB 未満はパーティション分割しない。各パーティション1 GB 以上見込めるなら分割可。新規は液体クラスタリング。
- AQE の4機能: ブロードキャストへの動的変更・パーティション動的結合・スキュー動的処理・空リレーション検出。有効のまま推奨。
- Photon: SQL API 互換の高速エンジン。SQL ウェアハウス既定有効、サーバーレスジョブで自動有効。
- UDF より組み込み/ネイティブ関数: Python UDF は Python↔Spark シリアル化で遅い。必要なら Pandas UDF(Arrow)。
- より大きいクラスター vs オートスケーリング: 線形スケールなら大きいクラスターは「速いだけで同コスト」。コスト最優先ならオートスケーリング(最速とは限らない)。
- small Spark jobs: 小さい操作が多数 → 並列化(複数タスクジョブ/SQL ウェアハウス/for each +コンカレンシー、Lakeflow パイプラインは自動)。
- DBR バージョン境界(頻出): パフォーマンス強化は 10.4 LTS 以降既定。液体クラスタリング GA は Delta 15.4 LTS / Iceberg 16.4 LTS。
OPTIMIZE FULLは 16.0 以降。自動液体クラスタリングは 15.4 LTS 以降。
6. 理解度チェックリスト
- [ ]
OPTIMIZEが何をするか(ビンパッキング/コンパクション)と、べき等 であることを説明できる - [ ] small files 問題の原因と、
OPTIMIZE/自動圧縮/最適化された書き込みでの対処を説明できる - [ ]
ZORDER BYと液体クラスタリングの違い、そして新規テーブルで液体クラスタリングが推奨される理由を説明できる - [ ] 液体クラスタリングが「既存データを書き換えずキーを再定義できる」「増分的」であることを説明できる
- [ ] クラスタリングキーは 最大4つ・統計収集列から選ぶ ことと、キー選択の指針を挙げられる
- [ ]
CLUSTER BY AUTO(自動液体クラスタリング)の条件(Unity Catalog マネージド)と仕組みを説明できる - [ ]
OPTIMIZE FULLを使う場面(初回有効化・キー変更時)を説明できる - [ ] 予測的最適化が自動実行する操作(
OPTIMIZE/VACUUM/ANALYZE)と有効化推奨の理由を説明できる - [ ]
VACUUMの目的、既定保持期間 7日、保持を短くする危険、タイムトラベルへの影響を説明できる - [ ] Spark UI で ジョブ/ステージ/タスク の関係を説明できる
- [ ] Spark UI で スキュー を判定する基準(最大 vs 75 パーセンタイル)を説明できる
- [ ] スピル の原因(メモリ不足)と、シャッフルとの関係を説明できる
- [ ] シャッフル が何で、なぜコストが高いか、線形スケーラビリティとの関係を説明できる
- [ ] AQE の4機能と、スキュー/ブロードキャスト結合との関係を説明できる
- [ ] ディスクキャッシュ/Spark キャッシュ/クエリ結果キャッシュの使い分け(Spark キャッシュ回避)を説明できる
- [ ] 過剰パーティション分割の弊害と、パーティション分割の目安(1 TB / 1 GB)を説明できる
- [ ] Photon が何で、どこで既定有効かを説明できる
- [ ] Python UDF を避けネイティブ関数/Pandas UDF を使う理由を説明できる
- [ ] 「より大きいクラスター」と「オートスケーリング」のコスト・速度トレードオフを説明できる
- [ ] 多数の小規模 Spark ジョブへの対処(並列化・SQL ウェアハウス・for each・Lakeflow)を説明できる
- [ ] OOM/スピル/スキューを Spark UI・イベントログで診断し、対処の方向性を挙げられる