Module 16 · Query Optimization Home · Engineering Guide · Troubleshooting · Playbook
Lesson 07 of 12 · ← Anti-Patterns · Next: Cost-Based Optimizer → · Performance Lab
Every technique in this module is a tool. This lesson is the process that tells you which tool to reach for, in what order, so tuning becomes repeatable instead of improvised.
1. MEASURE → 2. READ THE PLAN → 3. FORM A HYPOTHESIS
↓
5. VERIFY IMPACT ← 4. CHANGE ONE THING
flowchart LR
A[1. Measure] --> B[2. Read Plan]
B --> C[3. Hypothesize]
C --> D[4. Change One Thing]
D --> E[5. Verify Impact]
E -->|Not improved| C
E -->|Improved| F[Document & Ship]
Get an actual baseline — EXPLAIN ANALYZE, not a guess, not “it feels
slow.” Note actual elapsed time, not just estimated cost.
Find the single most expensive node (Lesson 02). Resist the urge to “fix”
something before you’ve identified the actual bottleneck — a common mistake
is optimizing a JOIN when the real cost is a downstream Sort for
ORDER BY.
Match the expensive node to a known cause:
| Symptom in plan | Likely cause | Relevant lesson |
|---|---|---|
Full table scan (Seq Scan — PostgreSQL; type: ALL — MySQL) on a large, filtered table |
Missing index, or non-SARGable predicate | 03 |
| Large gap between estimated and actual rows | Stale statistics | 01, 02 |
Nested Loop with no index on the inner side |
Missing join-column index | 04 |
Unexpectedly expensive Hash/build step |
Join order or missing predicate pushdown | 04 |
Sort before a LIMIT/ORDER BY |
Missing index matching the sort order | 03, 06 |
| Subquery/correlated-subquery node with high cost | Rewrite as JOIN/EXISTS/window function | 05 |
Make exactly one change — add one index, rewrite one predicate, restructure one subquery. Changing multiple things at once means you can’t attribute the improvement (or regression) to a specific cause, and you lose the ability to build a reliable mental model for next time.
Re-run EXPLAIN ANALYZE and compare directly against your Step 1 baseline.
If it didn’t help, revert the change and return to Step 3 with a new
hypothesis — don’t stack unproven changes on top of each other.
Waiting for a user complaint is the slowest possible way to find a slow query. Production teams typically:
slow_query_log, PostgreSQL’s
log_min_duration_statement) to capture anything over a threshold
automaticallypg_stat_statements on PostgreSQL) to catch a
plan that silently got worse after a statistics change or data growthANALYZE / UPDATE STATISTICS on a schedule so the optimizer’s
cost estimates stay accurate as tables growEXPLAIN alone on an already-slow
query, instead of EXPLAIN ANALYZE’s actual numbersTuning a single query is workload-unaware by design — Lesson 07’s
five-step loop optimizes one query at a time. At the workload level, teams
also need to watch for the aggregate effect of many “individually fine”
queries competing for the same resources (connection pool limits, cache
contention, I/O bandwidth) — a class of problem query-level EXPLAIN
tuning alone won’t surface.
Query tuning is a loop, not a single action: measure, read the plan, form a hypothesis grounded in the patterns from this module, change one thing, verify. This workflow is what turns Lessons 01–06 from a list of trivia into an applied engineering skill.
EXPLAIN ANALYZE, and walk
through all five workflow steps even if the query is already fast —
practice the process on a low-stakes example first.The examples below are illustrative composites showing how the five-step workflow applies at scale — they are not sourced claims about these specific companies’ internal practices. For a sourced, cited example, see the Cloud Cost lesson’s Shopify BigQuery case, which links a real Shopify Engineering blog post.
pg_stat_statements on PostgreSQL or the slow query log on MySQL), plan regressions flagged automatically.ANALYZE on all order tables and baseline EXPLAIN ANALYZE snapshots for the top 20 queries, as part of a load-event runbook.EXPLAIN ANALYZE snapshot in the PR description before merge.EXPLAIN ANALYZE output — future you (or the next engineer) needs the baselineEXPLAIN ANALYZE, not developer intuition — “feels slow” is not a baseline.pg_stat_statements, slow query log) so the next regression is caught before users complain.“Walk me through how you’d tune a slow query” is the capstone interview question for this module. Strong candidates follow the five steps in order, mention checking for lock contention before assuming a plan problem, and emphasize changing one thing at a time. Mentioning proactive monitoring (pg_stat_statements, Query Store) signals you’ve operated in production, not just studied theory.
pg_stat_statements (PostgreSQL) or slow query log (MySQL) on a dev instance and identify the top 5 queries by total time.EXPLAIN ANALYZE baseline today; after your next bulk data import, re-run and check whether the plan changed without any SQL modification.01_Query Execution Lifecycle · 02_EXPLAIN · 06_Anti-Patterns
pg_stat_statements extension