← All bricks

PostgreSQL · Intermediate

Stale Statistics After a Data-Distribution Change

A production incident, replayed by hand: after a bulk import the planner's row estimates went wrong several-fold, and the reflex fix — an index — would not have helped. Bring up the same database, catch the misestimate with EXPLAIN, confirm the statistics are stale, and fix it with one ANALYZE.

Query plans on the orders table went bad with no deploy and no schema change. A big historical import landed earlier, somebody already tried adding an index — and it changed nothing. This book replays the incident on a database you bring up yourself: the schema, the data, and the broken state are all in plain SQL files, so nothing is hidden. You reproduce the traffic, find the hot query with pg_stat_statements, and read the real clue in EXPLAIN (ANALYZE): the planner's row estimate disagrees with reality several-fold. pg_stat_user_tables confirms why — millions of rows changed since the last ANALYZE and autovacuum is off, so nothing ever refreshed the statistics. The fix is one ANALYZE, and you prove it by watching estimates line up with actuals. The diagnosis path is the real one you'd use on call.

What you'll build

  • Reproduce a planner misestimate on a local PostgreSQL stand
  • Find the hot query with pg_stat_statements
  • Read EXPLAIN (ANALYZE, BUFFERS): estimated rows vs actual rows
  • Check statistics freshness with pg_stat_user_tables
  • Fix stale statistics with ANALYZE — and resist the index reflex

Contents

  1. The stand
  2. Bring it up
  3. The schema
  4. Apply the schema
  5. The data, in two eras
  6. Load it
  7. A clean slate for statistics
  8. Watch the endpoint work
  9. Ask the database what hurts
  10. Ask the data itself
  11. Ask the planner — and catch it lying
  12. How old is the planner's worldview
  13. Design the fix
  14. Apply it
  15. Estimates meet reality
  16. Close the ticket