Skip to main content

aidp-spark-optimization

Use when a Spark job/notebook is slow, missing an SLA, spilling, OOMing, generating too many small files, or shuffling/skewing heavily; when reviewing Spark/PySpark code or a Spark UI for performance; or before running a large Spark workload. Covers open-source Apache Spark 3.5.0 (+ Delta Lake) tuning -- partitions, shuffle, joins, skew, file layout, memory, codegen, caching, AQE, compression, and configuration.

Zur Installation springen

Quellinformationen

Repository
oracle-samples/oracle-aidp-samples
Letzte Quellaktivität
24. Juni 2026 um 07:21
Erkannte Sprache von SKILL.md
Englisch
Sterne
46
Forks
30

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
16 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
aidp-spark-optimization
description
Use when a Spark job/notebook is slow, missing an SLA, spilling, OOMing, generating too many small files, or shuffling/skewing heavily; when reviewing Spark/PySpark code or a Spark UI for performance; or before running a large Spark workload. Covers open-source Apache Spark 3.5.0 (+ Delta Lake) tuning -- partitions, shuffle, joins, skew, file layout, memory, codegen, caching, AQE, compression, and configuration.
# Spark Optimization Field-tested techniques for optimizing **open-source Apache Spark 3.5.0** (and open-source Delta Lake) workloads. Every technique lists what it does, why it matters, **patterns** (when it helps), **anti-patterns** (when it does not help or hurts), the exact config/code, and impact evidence from real reproductions. Applies to plain OSS Spark 3.5.0. Vendor-only features (Databricks Photon / predictive optimization / Delta disk cache, AWS Glue/EMR runtime tricks) are excluded or explicitly flagged. (Note: Delta Lake liquid clustering, OPTIMIZE/ZORDER, deletion vectors, CDF are open-source — GA in Delta 3.2.0 — see `references/08-delta-lake.md`.) Some examples run on the Oracle AI Data Platform (AIDP); AIDP-specific execution rules are in `references/aidp-notes.md`. > **In this plugin (`oracle-ai-data-platform-workbench-engineer-agent`):** measure/apply on AIDP via the > bundled `$HOME/.aidp/aidp_sql.py` (one SparkSession per cluster -- set/revert `spark.conf` explicitly; see > `references/aidp-notes.md`); pull Spark-UI stage/task metrics via the **`aidp-spark-debugging`** skill; > right-size compute + set cluster-create-only configs (e.g. `spark.memory.fraction`) via **`aidp-cluster-ops`**; > run Delta `OPTIMIZE`/`VACUUM`/`ZORDER` DDL via **`aidp-sql-ddl`** / **`aidp-table-management`**. *Adapted from > the `ai-data-engineer-agent` `spark-optimization` skill (Oracle).* ## Core principle > **Optimization is a measurement loop, not a guess.** Detect → diagnose → propose → apply → re-run on the same data → compare the same metrics. Most Spark time is lost to **moving data** (shuffle), **waiting on a straggler** (skew), **spilling** (under-memory), **doing redundant work** (re-reads, re-materialization, repeated actions), or **per-row execution overhead** (wide codegen). Almost every win below reduces one of those. ## When to use - A job/notebook is slow, missing an SLA, or its cost is too high. - Spark UI shows: a single slow stage, task `p100/p50 > 2x` skew, large shuffle read/write, memory/disk spill, high GC, or thousands of tiny tasks/files. - You see code smells: `union` of the same table, a driver loop calling `collect()`/`head()`/`count()` repeatedly, `MERGE` on a huge table, wide `groupBy` (100+ fields), reading 100k+ small files, an un-filtered join with a big dimension. - You read from / write to an **Oracle database** (ADW/ATP/Exadata): a single-task JDBC read, a slow or redo-saturating multi-TB write, or unsupported array/map/struct columns. (`references/09-oracle-database.md`) - Before launching a large ingest/transform so the cluster + configs are right the first time. **When NOT to use:** correctness bugs (use debugging), or sub-second jobs where tuning effort exceeds the payoff. Structured Streaming *engine* tuning (trigger interval, `maxOffsetsPerTrigger`, state store) is out of scope here — but each micro-batch is batch-shaped, so the join/skew/partition/file/codegen techniques below apply per batch. ## How to find the opportunity → which reference to read ```dot digraph route { rankdir=LR; node [shape=box]; sym [shape=diamond, label="Dominant symptom?"]; sym -> "references/diagnosis.md" [label="not sure / need to measure"]; sym -> "references/02-joins.md" [label="slow join, skew, SortMergeJoin"]; sym -> "references/04-memory-and-spill.md" [label="spill, OOM, lost executors, GC"]; sym -> "references/cluster-sizing.md" [label="right-size compute: workers/OCPU/RAM"]; sym -> "references/03-file-layout-io.md" [label="many small files, slow scan, listing"]; sym -> "references/01-partitioning.md" [label="too many/few partitions, weak parallelism"]; sym -> "references/05-codegen.md" [label="CPU-bound wide aggregation"]; sym -> "references/06-caching-materialization.md" [label="re-reads, union, driver loop, MERGE"]; sym -> "references/07-aqe.md" [label="want runtime adaptivity"]; sym -> "references/08-delta-lake.md" [label="Delta table: small files, OPTIMIZE/ZORDER/clustering, VACUUM, deletion vectors, CDF"]; sym -> "references/09-oracle-database.md" [label="read/write Oracle DB / ADW / Exadata (JDBC parallelism, redo, complex types)"]; } ``` Then consult `references/config-matrix.md` for the exact key, default, impact, and **where it can be set** (notebook / cluster-create-only / non-modifiable), and `references/quick-reference.md` for the impact-ranked checklist. ## Reference index | File | Covers | |---|---| | `references/diagnosis.md` | How to find & measure opportunities from the Spark UI: symptom→cause, skew ratio, which metrics, query→stage timing. | | `references/01-partitioning.md` | Partition counts, parallelism, `shuffle.partitions`, AQE coalescing, tiny/empty partitions, `repartition` vs `coalesce`. | | `references/02-joins.md` | Broadcast join, Shuffle-Hash vs Sort-Merge, skew handling, semi-join pre-filter, salting. | | `references/03-file-layout-io.md` | Small-file problem, `openCostInBytes`/`maxPartitionBytes`, compaction/OPTIMIZE, `partitionOverwriteMode`, gzip splittability, compression (zstd). | | `references/04-memory-and-spill.md` | Spark memory model, `memory.fraction`, spill detection/fix, off-heap, the gzip-4x rule. | | `references/cluster-sizing.md` | Right-size AIDP compute: workers x OCPU x RAM, parallelism vs memory, observed-run → recommendation. | | `references/05-codegen.md` | WholeStage codegen, `codegen.maxFields`, when generated code is *slower*. | | `references/06-caching-materialization.md` | Caching & cache layout, avoid unnecessary materialization, union-of-same-table, driver-loop, JOIN-over-MERGE. | | `references/07-aqe.md` | Adaptive Query Execution: coalesce, skew join, the threshold interplay. | | `references/08-delta-lake.md` | Delta Lake 3.2.0: OPTIMIZE/compaction (+VACUUM dependency), optimized writes, auto-compaction, ZORDER, liquid clustering, deletion vectors, CDF, data skipping, file sizing; the optimizeWrite+zstd+binSize recipe. | | `references/09-oracle-database.md` | Oracle DB (ADW/ATP/Exadata): parallel JDBC reads (`partitionColumn`, `fetchsize`), bulk (COPY_DATA, ADW-only) vs conventional-JDBC writes, the redo-log / NOLOGGING field case + tradeoffs, complex types (JSON-as-VARCHAR2), Storage Partition Join. | | `references/config-matrix.md` | Master config table: default, impact, and where-settable classification. | | `references/case-studies.md` | Real before/after numbers (field engagements + AIDP reproductions). | | `references/quick-reference.md` | Impact-ranked checklist + symptom→technique lookup. | | `references/aidp-notes.md` | AIDP execution model + how to set/revert configs safely (one SparkSession per cluster) + collect-metrics-early. | ## The 80/20: highest-impact moves first Ranked by typical payoff (full details + caveats in the referenced files): 1. **Eliminate avoidable shuffle on joins** — raise `spark.sql.autoBroadcastJoinThreshold` so a small/medium side broadcasts; or **semi-join pre-filter** the big dimension down to broadcastable size. (`02-joins.md`) 2. **Fix the small-file problem** — compact sources / size output files; tune `maxPartitionBytes` + `openCostInBytes`. Listing+opening 100k+ files can cost more than the data. (`03-file-layout-io.md`) 3. **Right-size memory to stop spill** — raise `spark.memory.fraction` (cluster-create-only on AIDP) when there are no user data structures; spill to disk is a silent multiplier. (`04-memory-and-spill.md`, `cluster-sizing.md`) 4. **Address skew** — AQE skew-join, or semi-join pre-filter + broadcast; a `p100/p50 > 2x` join stage serializes the whole stage on a straggler. (`02-joins.md`, `07-aqe.md`) 5. **Avoid redundant work** — don't `union` the same table, don't loop `collect()`/`head()` on the driver, treat intermediates as views (don't materialize), prefer JOIN over MERGE for full reloads. (`06-caching-materialization.md`) 6. **Compress shuffle/spill/output with zstd** — `spark.io.compression.codec=zstd` + `spark.sql.parquet.compression.codec=zstd`: tight like gzip, fast like snappy; cuts spill-disk and output size. (`03-file-layout-io.md`) 7. **Tune parallelism** — `shuffle.partitions` ≈ 2–3× total cores for large shuffles; pair with AQE coalescing to avoid small output files. (`01-partitioning.md`) 8. **Constrain codegen on very wide aggregations** — lower `spark.sql.codegen.maxFields` when a wide `groupBy` is CPU-bound; generated code can be *slower* than the fallback. (`05-codegen.md`) ## Workflow 1. **Measure** — run the workload, collect stage metrics + task quantiles (`diagnosis.md`). On AIDP, see `aidp-notes.md` for collecting Spark UI data. 2. **Diagnose** — read `explain(True)`/the SQL plan; map the dominant symptom to a technique. 3. **Propose** — pick the technique; check `config-matrix.md` for where the config can be set. 4. **Apply** — change code or config. On a shared cluster, remember: **one SparkSession per cluster — a `spark.conf.set` leaks to other notebooks; revert it explicitly** (`aidp-notes.md`). 5. **Re-run** on the same cluster + data, prove the workload is logically identical (same rows/columns/result), and **compare the same metrics** (wall time, shuffle, spill, task skew). ## Common mistakes - Comparing wall time of two runs that aren't logically identical (different data/cache state). Prove equivalence first. - Reading summed task metrics as wall-clock — stage duration is start→end timestamps; cumulative task time is summed across tasks. - Counting AQE-skipped stages as time spent. - `repartition()` "to be safe" — it forces a shuffle; only repartition with a reason (see `01-partitioning.md`). - Lowering AQE skew/advisory partition size so far that you create a new small-file problem. - Setting a cluster-create-only config (e.g. `spark.memory.fraction`) from a notebook and assuming it took effect — it didn't. Verify with `spark.conf.get` / the Environment tab (`config-matrix.md`).
Auf GitHub ansehen