Lesson 4 of 9 · 20m
EXPLAIN Fluency: Reading Query Plans
EXPLAIN ANALYZE in five minutes — scan types, join strategies, and the three signals that tell you a query is about to be a problem.
EXPLAIN ANALYZE runs your query for real and shows the plan with actual timings — it's the single most leveraged debugging tool in Postgres. You don't need to master the whole plan tree; you need to recognize three shapes.
The scan shapes
- Seq Scan — reads every row. Fine for small tables or queries matching most of the table; a red flag on a filtered query over a big table.
- Index Scan — uses the index to find rows, then fetches them from the heap. Fast for selective predicates.
- Index Only Scan — the index itself answers the query; never touches the heap. The fastest read Postgres offers.
- Bitmap Heap Scan — a middle ground: the index finds many candidate rows, and they're fetched in physical order (cheaper than random access when the match set is large-ish).
Join strategies
- Nested Loop — for each outer row, probe the inner table (usually via index). Great when the outer side is small.
- Hash Join — build a hash of the smaller side, probe with the larger. The default for big joins on equality.
- Merge Join — both sides sorted, walk them in lockstep. Used when inputs are already ordered (e.g., by index).
What to look for first
EXPLAIN (ANALYZE, BUFFERS) SELECT ...;- Rows estimated vs actual —
rows=100estimated,rows=180000actual means the planner is flying blind (stale stats, correlated columns). A bad estimate poisons every decision downstream. - Execution time and the hot node — find the node eating the time; the plan tree reads bottom-up.
- Buffers hit vs read —
shared hitis cache,readis disk. A "fast" query reading from disk is one big table away from being slow.
::exercise(read-the-plan)
::lab(explain-yourself)
Field notes
EXPLAINwithout ANALYZE shows the estimated plan — enough to check the shape without touching the table.auto_explainwithlog_min_durationcaptures plans for slow queries in production — set it up before you need it.work_memtoo small forces hash joins to spill to disk (Batches: 8in the plan) — memory tuning often fixes "slow join" instantly.