Overview
PostgreSQL’s MVCC leaves the old row version behind after every UPDATE and DELETE. VACUUM makes that space reusable, updates the free space and visibility maps (which enable Index Only Scans), and freezes old transaction IDs to prevent wraparound. Autovacuum runs VACUUM and ANALYZE per table once thresholds pass. Tune it per table instead of scheduling manual runs, and never disable it in production.
Rules
- Vacuum triggers at
autovacuum_vacuum_threshold(50) plusautovacuum_vacuum_scale_factor(0.2) times the row count, capped atautovacuum_vacuum_max_threshold(100 million, new in PostgreSQL 18). Analyze triggers at 50 plus 0.1 times the row count. - Lower the scale factor on large, high-churn tables; 20 percent of a big table is a huge dead-tuple backlog.
- Long transactions,
idle in transactionsessions, old prepared transactions, and stale replication slots hold back cleanup and freezing. Find them inpg_stat_activityandpg_replication_slots. - If autovacuum falls behind on fast storage, raise
autovacuum_vacuum_cost_limitor lowerautovacuum_vacuum_cost_delay(default 2 ms). - Plain
VACUUMruns alongside reads and writes and usually does not return space to the OS.VACUUM FULLrewrites the table under anACCESS EXCLUSIVElock that blocks all access; use it only in a maintenance window. - Near wraparound PostgreSQL warns (40 million transactions left), then refuses commands that assign transaction IDs (3 million left) until a database-wide vacuum.
SELECT relname, n_live_tup, n_dead_tup, last_autovacuum, last_autoanalyze
FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 10;
ALTER TABLE events SET (autovacuum_vacuum_scale_factor = 0.02, autovacuum_analyze_scale_factor = 0.01);Related
- query-plan:
ANALYZEkeeps planner statistics current. - sequential-scan: stale statistics cause bad scan choices.
- write-ahead-log: vacuum generates WAL.
- postgres-vacuum: tuning in depth.
- postgres: the Postgres reference.