Databricksでのクエリパフォーマンスは「ファイルサイズの適正化」「AQE(Adaptive Query Execution)の活用」「Join戦略の選択」「Spill(ディスク溢出)の回避」の4軸で大きく改善します。 本稿では各軸の設計パターンとパラメータ設定を、Data Engineer Professional試験と実務の両面から整理します。
Delta LakeやParquetのファイルサイズは、読み取りパフォーマンスに直結します。 ファイルが小さすぎるとメタデータオーバーヘッドとタスク起動コストが増大し(Small File Problem)、 大きすぎるとパーティション内の並列度が下がります。
| パラメータ / 操作 | 目的 | 推奨値 |
|---|---|---|
| OPTIMIZE | 小さなファイルを結合して最適サイズに圧縮 | ターゲットサイズ 128MB(デフォルト 1GB、テーブル特性で調整) |
| delta.targetFileSize | OPTIMIZEのターゲットファイルサイズ | 128MB〜256MB(読み取りパターンに応じて調整) |
| spark.sql.files.maxPartitionBytes | 読み取り時に1タスクが処理する最大バイト数 | 128MB(デフォルト) |
| spark.sql.files.openCostInBytes | ファイルオープンコストの見積もり | 4MB(デフォルト) |
| autoOptimize(autoCompact) | 書き込み後に自動で小ファイルを圧縮 | ストリーミング書き込みテーブルで有効化推奨 |
-- OPTIMIZEの実行(ZORDERと組み合わせ可能)
OPTIMIZE gold.orders ZORDER BY (customer_id, order_date);
-- テーブルプロパティでターゲットサイズを設定
ALTER TABLE gold.orders
SET TBLPROPERTIES (
'delta.targetFileSize' = '134217728' -- 128MB
);
-- autoOptimizeの有効化
ALTER TABLE silver.events
SET TBLPROPERTIES (
'delta.autoOptimize.optimizeWrite' = 'true',
'delta.autoOptimize.autoCompact' = 'true'
);ZORDER BYはフィルタ条件に頻出するカラムを指定します。 ZORDERはカラム値でデータを空間的にクラスタリングし、Data Skippingの効果を最大化します。 ただし、ZORDERは高カーディナリティのカラム(UUIDなど)には効果が薄いため、3〜4カラム以下に絞ります。
AQEは実行時の統計情報に基づいてクエリプランを動的に最適化する機能です。 Databricksではデフォルトで有効であり、以下の3つの最適化を自動的に行います。
| パラメータ | デフォルト値 | 説明 |
|---|---|---|
| spark.sql.adaptive.enabled | true | AQE全体の有効/無効 |
| spark.sql.adaptive.coalescePartitions.enabled | true | 小パーティション結合の有効/無効 |
| spark.sql.adaptive.coalescePartitions.minPartitionSize | 1MB | 結合後のパーティション最小サイズ |
| spark.sql.adaptive.advisoryPartitionSizeInBytes | 64MB | 結合後の推奨パーティションサイズ |
| spark.sql.adaptive.skewJoin.enabled | true | スキューJoin最適化の有効/無効 |
| spark.sql.adaptive.skewJoin.skewedPartitionFactor | 5 | 中央値の何倍でスキュー判定するか |
| spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes | 256MB | スキュー判定の絶対閾値 |
| spark.sql.adaptive.autoBroadcastJoinThreshold | 30MB | AQEがBroadcast Joinに切り替えるサイズ閾値 |
SparkのJoin戦略は主に3種類あり、テーブルサイズとデータ特性に応じて選択されます。 AQEが有効な場合は実行時に動的に切り替わりますが、設計段階での理解が重要です。
| Join戦略 | 条件 | メリット | 注意点 |
|---|---|---|---|
| Broadcast Hash Join | 片方のテーブルがautoBroadcastJoinThreshold以下 | Shuffleなし。最速 | 大テーブルをBroadcastするとDriverのOOM |
| Sort-Merge Join | 両方のテーブルが大きい場合のデフォルト | 安定。大テーブル同士で確実に動作 | ソート+Shuffleのコストが高い |
| Shuffle Hash Join | 一方がやや大きく、もう一方がメモリに収まる場合 | Sort不要でSort-Mergeより速い場合がある | メモリ消費が大きく、Spillリスク |
-- 小テーブルを明示的にBroadcastする(ヒント指定)
SELECT /*+ BROADCAST(dim_products) */
f.order_id,
f.amount,
d.product_name
FROM fact_orders f
JOIN dim_products d ON f.product_id = d.product_id;
-- autoBroadcastJoinThresholdの調整
SET spark.sql.autoBroadcastJoinThreshold = 50m; -- 50MBまでBroadcastBROADCASTヒントはオプティマイザの判断を上書きします。 ディメンションテーブル(数万〜数十万行)とファクトテーブル(数億行)のJoinでは、ディメンション側にBROADCASTヒントを指定するのが定石です。
spark.sql.shuffle.partitionsはShuffle時のパーティション数を制御します。デフォルトは200ですが、データ量に応じて調整が必要です。
-- Shuffleパーティション数の調整
SET spark.sql.shuffle.partitions = 400;
-- AQE有効時の推奨パーティションサイズ
SET spark.sql.adaptive.advisoryPartitionSizeInBytes = 128m;SpillはShuffle、Sort、Aggregation時にメモリが不足した場合にデータをディスクに退避する動作です。 パフォーマンスが大幅に劣化するため、Spillの発生を最小化する設計が重要です。
| 対策 | 方法 | 効果 |
|---|---|---|
| パーティション数の増加 | spark.sql.shuffle.partitions を増やす | 各タスクのデータ量を減らしメモリ内処理に収める |
| メモリ増強 | メモリ最適化インスタンスタイプに変更 | executor当たりのメモリ容量を拡大 |
| Broadcast Joinの活用 | 小テーブル側をBroadcastしてShuffleを回避 | Shuffle自体を排除 |
| データの事前フィルタリング | JOIN前にWHERE句で不要データを除外 | Shuffle対象データ量を削減 |
| スキュー対策 | AQEのskewJoinを有効化、またはsaltingテクニック | 偏ったパーティションを分散 |
-- Spark UIで確認すべきメトリクス
-- Stages > Task Details:
-- Shuffle Spill (Memory): メモリからディスクに溢れたデータ量
-- Shuffle Spill (Disk): ディスクに書き込まれたデータ量
-- Spillが発生している場合の対策例
SET spark.sql.shuffle.partitions = 800; -- パーティション分割を細かく
SET spark.sql.adaptive.advisoryPartitionSizeInBytes = 64m; -- AQE推奨サイズを小さくPhotonはDatabricksのネイティブベクトル化実行エンジンで、Scan、Filter、Aggregation、Joinの各ステージを高速化します。 Photon対応のクラスターを使用するだけで有効になり、特別なSQLの書き換えは不要です。
Data Engineer Professional
問題 1
10億行のファクトテーブルと5万行のディメンションテーブルをJoinするクエリが遅い。Spark UIを確認するとSort-Merge Joinが選択され、Shuffle Spill (Disk) が大量に発生している。最も効果的な改善策はどれか。
正解: A
5万行のディメンションテーブルはBroadcast可能なサイズです。BROADCASTヒントでBroadcast Hash Joinに切り替えると、Shuffleが排除されSpillも解消します。パーティション数を減らすとタスク当たりのデータ量が増えSpillが悪化します。AQE無効化は最適化機能を失います。ZORDERをJoinキー以外で実行してもJoinのShuffle/Spillには影響しません。
OPTIMIZEはどのタイミングで実行すべきですか?
バッチ書き込み完了後、下流のクエリが実行される前が最適です。典型的にはETLジョブの末尾にOPTIMIZEタスクを入れるか、日次のメンテナンスジョブとしてスケジュールします。ストリーミング書き込みのテーブルでは、Databricksの自動最適化(autoOptimize)を有効にすることで手動実行を減らせます。頻繁にOPTIMIZEを実行するとコンピュートコストが増えるため、読み取りパフォーマンスとのバランスで頻度を決めます。
AQEはデフォルトで有効ですか?無効にするケースはありますか?
Databricks Runtime 12.x以降ではAQE(spark.sql.adaptive.enabled)はデフォルトで有効です。AQEを無効にすべきケースは稀ですが、ベンチマーク時にAQEの影響を除外したい場合や、AQEのパーティション結合が特定クエリで逆効果になる場合(検証で確認)に限定的に無効化します。通常の運用では有効のまま推奨です。
Spill(ディスク溢出)が発生しているかどうかはどこで確認できますか?
Spark UIのStagesタブで各タスクのShuffle Spill (Memory) と Shuffle Spill (Disk) を確認できます。Spill (Disk) が0でなければディスクへの溢出が発生しています。また、SQLタブのクエリ実行計画でSortやHashAggregateノードのspill metricsも確認できます。定常的にSpillが発生するクエリでは、spark.sql.shuffle.partitionsの調整やメモリ増強を検討します。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Databricks資格一覧|全7試験・難易度・勉強法
Databricks認定資格全7試験の一覧・難易度・出題範囲・合格ラインを徹底解説。2026年最新版の公式試験ガイドに準...
Databricks試験の難易度ランキング|全7資格を徹底比較
Databricks認定全7試験の難易度をランキング形式で徹底比較。合格率・学習時間・出題傾向から難易度を分析。...
Databricks資格の勉強方法|最短合格ルートと学習時間の目安
Databricks認定資格に最短で合格するための勉強方法を完全ガイド。公式リソース・問題集・学習スケジュールを徹底解説...
Databricks Data Engineer Associate完全解説|出題範囲・問題例・合格戦略
Databricks Certified Data Engineer Associate試験を徹底解説。5つの出題ドメイ...
Databricks Data Engineer Professional完全解説|上級試験の攻略法
Databricks Certified Data Engineer Professional試験を徹底解説。10の出題...