BXUP Track progress & test yourself — free
Data Engineering · Playbook · Core · ~4 min read
DATA ENGINEERING · PLAYBOOK · CORE

Tune Slow Queries and Spark Jobs from Plans and Metrics

Reading an EXPLAIN plan or a Spark UI is a skill most teams have in exactly one person. LLMs read them fluently: hand one the plan plus table statistics and it will spot the unpruned scan, the exploding join, the missing clustering — and propose fixes.

Tier: Core · Workflow area: Orchestration, ops & cost

The catch is that plausible tuning advice is cheap and wrong tuning advice costs real money, so this playbook wraps the AI diagnosis in the only loop that matters: measure before, change one thing, measure after.

When to reach for this

  • A warehouse query or model went from minutes to hours, or lands on the cost report's top-10.
  • A Spark job shows the classic pathologies — one straggler task, shuffle spill, executor OOM — and nobody on the team is a Spark-UI whisperer.
  • A cost-triage pass (see pb-25) has ranked offenders and you now need per-query fixes.

Skip it for queries that are slow because they process what they must — AI can't optimize physics, and "make it faster" on an already-tight plan yields confident noise.

The workflow

  1. Capture the evidence, not a description of it. For warehouse queries: the query text, the actual execution plan or profile (post-execution profile beats estimated EXPLAIN — it has real row counts and spill), and table facts: row counts, partition/cluster keys, sizes. For Spark: the failing/slow stage's task-duration distribution, shuffle read/write sizes, spill metrics, and the job's config (executor memory/cores, AQE settings) — exported text/JSON from the Spark UI, not a screenshot description.

  2. Baseline first. Record current runtime, bytes scanned, and cost (warehouse credit or cluster-minutes). Without the baseline the after-measurement is theater.

  3. Prompt for ranked diagnoses tied to plan evidence.

    Diagnose this slow query from the attached execution profile and table stats.
    
    Rules:
    - Cite the specific plan node / metric that supports each diagnosis
      (e.g. "join node 7: build side 400M rows, estimated 10k — stats stale or
      filter not pushed").
    - Rank findings by expected impact; give ONE recommended change per finding.
    - Distinguish query-level fixes (rewrite, filter pushdown, join order) from
      table-level fixes (clustering/partitioning, stats refresh) — table-level
      changes affect other workloads and cost money to maintain; say so.
    - For each recommendation, state the metric that should move and by roughly
      how much, so I can verify.
    - If the plan looks already-optimal, say so instead of inventing changes.
    

    For Spark, the same shape, with the fix vocabulary being broadcast hints vs. sort-merge, key salting for skew, partition-count changes, AQE toggles, and memory config — and the same demand: every diagnosis cites a metric (e.g. "max task 48 min vs. median 40 s in stage 12 → skew on join key").

  4. Apply one change, re-measure. One variable at a time, same data volume (rerun on the same partition/date), compare runtime + bytes scanned + spill against the baseline. If the predicted metric didn't move, the diagnosis was wrong — revert and take the next ranked finding back to the model with the new evidence. This loop usually converges in one to three iterations.

  5. Check the blast radius of table-level changes. Reclustering, repartitioning, or changing file sizes helps this query and taxes others (and clustering has ongoing maintenance cost). Before adopting, test the top other consumers of the table, and re-run the cost math over a week, not one execution.

  6. Bank the result. Record before/after numbers in the PR/ticket and add the pattern (e.g. "date filter must be on the partition column, not a function of it") to your team's SQL review notes — the LLM finds instances; the pattern note prevents recurrences.

Pro Tip

Give the model the executed profile with actual row counts, not the estimated EXPLAIN. Half of real-world slowness is actual-vs-estimate divergence (stale stats, correlated filters), and the model can only see that when both numbers are in front of it.

Verify before you trust it

  • Every accepted diagnosis cites a plan node/metric you can see in the profile — no citation, no change.
  • After-measurement on identical input shows the predicted metric moved (runtime, bytes scanned, spill), not just wall-clock luck on a quieter cluster.
  • Results are byte-identical (or explained): SELECT count(*), sum(hash(...))-style checksums on before/after outputs — a fast wrong query is still wrong.
  • Table-level changes were tested against the table's other top consumers and priced over a representative window.
  • On Spark: the fix survives a data-volume change (skew salting tuned to today's key distribution can regress next month — note the assumption).

Pitfalls

  • Generic-advice syndrome. Without plan evidence in the prompt, you get "add indexes, avoid SELECT *" boilerplate. Real profiles in, cited findings out.
  • Hint sprawl. Broadcast hints and forced join orders rot as data grows. Prefer fixing the cause (stats, filters, layout) over pinning the plan; document any hint with its assumption.
  • Optimizing the wrong cost. Halving runtime by quadrupling the warehouse size is a bill increase with good vibes. Always carry cost next to runtime in the before/after.
  • Trusting one run. Cache effects and cluster contention make single-run comparisons noisy. Two-to-three runs, or force cold-cache semantics, before declaring victory.
  • Skipping correctness checks. Join-order and predicate rewrites can silently change semantics on NULLs and duplicates. Checksum the outputs, every time.

Tools

  • Databricks Genie Code (ex-Assistant)in-workspace diagnosis with the query profile and table context already attached; the lowest-friction path on Databricks.
  • Claude Code / Cursor (or Claude with pasted profiles)engine-agnostic; works for Snowflake/BigQuery profiles and exported Spark UI metrics alike.
  • Warehouse query profiler / Spark UIthe evidence source; the discipline of exporting real profiles is most of the skill.

Client-safe variant

Query text and plans reveal schema design and business logic; table stats reveal scale — treat all of it as client-confidential. Where external assistants aren't approved, use the platform-native option inside the client's tenancy (Genie on Databricks, the warehouse's own copilot) so nothing leaves the boundary; otherwise sanitize (rename tables/columns to neutral placeholders consistently — plan structure survives renaming) before prompting an external model, and keep the measurement loop entirely client-side. Report improvements in the client's currency — credits or cluster-hours saved per month at current volumes, with the before/after evidence — because "the query is faster" is an anecdote and "₹80k/month off the warehouse bill" is a deliverable.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in