--- name: postgres-explain-analyze description: Reading an EXPLAIN ANALYZE plan to find why a query is slow, rather than adding indexes hopefully. when_to_use: A query is slow, or you want to confirm an index is actually used. tags: [backend, postgres] --- # Read the plan; do not guess at indexes An index added without a plan is as likely to be unused as to help, and each one costs write throughput forever. ## Always ANALYZE, and usually BUFFERS ```sql EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT ...; ``` `EXPLAIN` alone shows the planner's *estimate*. `ANALYZE` executes and shows what happened, which is the only thing worth reading. `BUFFERS` shows whether the data came from cache or disk — a "slow" query that is entirely `shared read` is an I/O problem, not a plan problem. Note that `ANALYZE` actually runs the statement. Wrap a mutation in a transaction you roll back. ## Read it inside out, and look for two things Plans nest; the innermost node runs first. Scan for: 1. **The largest `actual time`,** not the largest estimate. That is where the time went. 2. **Estimate versus actual rows.** `rows=10` with `actual rows=48000` is the planner being wrong, and a wrong estimate is usually the *cause* of a bad plan — it picked a nested loop because it expected ten rows. Fix the statistics (`ANALYZE