EXPLAIN ANALYZE Cheatsheet (Postgres + MySQL)

Quick reference khi đang debug query slow. Bookmark này.

Tags: attachment cheatsheet explain Liên quan: Tuan-04-Query-Optimization-EXPLAIN · Tuan-Bonus-MySQL-Internals


1. Postgres — Syntax

EXPLAIN <query>;                                        -- chỉ plan
EXPLAIN ANALYZE <query>;                                 -- chạy thật, đo thời gian
EXPLAIN (ANALYZE, BUFFERS) <query>;                      -- + I/O stats
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, FORMAT JSON) <query>;-- full debug
 
-- Wrap trong transaction nếu test UPDATE/DELETE:
BEGIN; EXPLAIN (ANALYZE) UPDATE ... ; ROLLBACK;
 
-- PG16+: shows non-default settings
EXPLAIN (ANALYZE, SETTINGS) <query>;
 
-- PG13+: WAL volume per query
EXPLAIN (ANALYZE, WAL) <query>;
 
-- PG16+: GENERIC_PLAN cho prepared/parameterized
EXPLAIN (GENERIC_PLAN) <query>;

2. Postgres — Output Anatomy

Limit  (cost=0.43..8.55 rows=20 width=64) (actual time=0.025..0.075 rows=20 loops=1)
  ->  Index Scan using idx_x on orders  (cost=...) (actual time=...) (rows=20)
        Index Cond: (user_id = 42)
        Filter: (status = 'pending')
        Rows Removed by Filter: 30
        Buffers: shared hit=5 read=2
 Planning Time: 0.123 ms
 Execution Time: 0.105 ms
FieldMeaning
cost=X..YEstimated startup..total cost (abstract unit)
rows=NEstimated rows
width=NBytes per row
actual time=X..YReal startup..total ms
rows=N loops=MNode ran M times, returned N each
Index CondUsed by index (efficient)
FilterPost-scan filter (less efficient)
Rows Removed by FilterDiscarded rows (high = bad)
Heap Fetches (Index Only Scan)Fetch from heap; 0 = perfect
Buffers: shared hit/read/dirtiedBuffer pool stats
Sort Method: quicksortIn-memory sort
Sort Method: external sortDisk spill — bad
Batches: N (Hash)>1 = hash spill

3. Postgres — Red Flags Quick Scan

Red flagCauseFix
Seq Scan on huge tableNo indexAdd index
actual rows >> rows (10x+)Stats staleANALYZE
Heap Fetches > 0 in IO ScanVM staleVACUUM
Sort Method: external sortwork_mem too lowSET work_mem = '256MB'
Batches: 4+ in HashHash spillIncrease work_mem
loops > 1000 Nested LoopQuadraticForce Hash/Merge join
Buffers: shared read >> hitCold cacheTune shared_buffers
Planning Time >> Execution TimeComplex planPrepared statement
Rows Removed by Filter hugePost-index filterComposite/partial index

4. Postgres — 30-Second Diagnose

  1. Find slowest node (biggest actual time)
  2. Check rows estimate vs actual — mismatch >10x → stats issue
  3. Check scan type: Seq vs Index — appropriate?
  4. Check joins: Nested Loop with high outer rows → fix
  5. Check sort/hash: spill to disk?
  6. Check buffer hit: cold or hot?

5. Postgres — Plan Nodes Reference

Scans

  • Seq Scan — full table; fine if table small or >5-10% returns
  • Index Scan — index → heap fetch per row
  • Index Only Scan — read only from index; needs VM
  • Bitmap Heap Scan — combine indexes via bitmap
  • Tid Scan — direct CTID lookup
  • Foreign Scan — FDW

Joins (3 algorithms)

  • Nested Loop — for each outer, scan inner. Good for outer ≤ 100 + indexed inner.
  • Hash Join — build hash on smaller, probe larger. O(N+M). Bad if spill.
  • Merge Join — sorted both sides walk together. Good when sorted.

Other

  • Sort — order rows
  • Aggregate / HashAggregate / GroupAggregate
  • Limit — top N
  • Materialize — cache rows for reuse
  • Hash — build hash table
  • Gather / Gather Merge — parallel collection
  • CTE Scan — materialized CTE
  • SubPlan / InitPlan — uncorrelated subquery

6. Postgres — Useful Pragmatics

-- See top queries
SELECT substring(query,1,80), calls, mean_exec_time, total_exec_time
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;
 
-- Check stats freshness
SELECT relname, last_vacuum, last_autovacuum, last_analyze, n_dead_tup
FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 10;
 
-- Find unused indexes
SELECT indexrelname, pg_size_pretty(pg_relation_size(indexrelid))
FROM pg_stat_user_indexes WHERE idx_scan = 0
  AND indexrelname NOT LIKE '%_pkey';
 
-- Plan visualizer
-- Paste EXPLAIN (FORMAT JSON) output to:
-- https://explain.dalibo.com or https://explain.depesz.com

7. MySQL — Syntax

EXPLAIN <query>;                           -- basic
EXPLAIN FORMAT=JSON <query>;                -- detailed
EXPLAIN ANALYZE <query>;                    -- MySQL 8.0.18+, actual run
SHOW WARNINGS;                              -- planner notes

8. MySQL — Output Columns

ColumnMeaning
idStep number (higher = inner)
select_typeSIMPLE/PRIMARY/SUBQUERY/etc
tableTable accessed
typeAccess type (see below)
possible_keysIndexes available
keyIndex actually used
key_lenUsed index bytes
refWhat column compared
rowsEstimated rows examined
filtered% rows after filter
ExtraHints (Using index, Using temporary, etc.)

9. MySQL — Access Types (best → worst)

TypeMeaning
system1-row table
constConst lookup via PK/unique
eq_ref1-row from each table via PK/unique (joins)
refIndex lookup, multiple rows
rangeIndex range scan
indexFull index scan
ALLFull table scan — BAD

10. MySQL — Red Flags

ExtraMeaning
Using filesortSort can’t use index
Using temporaryTemp table created
Using join buffer (Block Nested Loop)No index for join
Using wherePost-filter (often OK)
Using indexCovering index — GOOD
Using index conditionICP optimization — GOOD
Impossible WHEREQuery returns nothing (planner knows)

11. Tools

  • Postgres: pgMustard, pev2 (dalibo), explain.depesz.com, auto_explain
  • MySQL: dbForge, MySQL Workbench Visual Explain, EVERSQL
  • Both: DBeaver

Cập nhật: 2026-05-16