VACUUM & Autovacuum
PostgreSQL's MVCC model (see the MVCC page) never overwrites a row in place when you UPDATE or DELETE it — instead it keeps the old row version around so concurrent transactions that started before your change can still see a consistent snapshot. Those old row versions are called "dead tuples," and once nothing needs them anymore, something has to clean them up. That something is VACUUM.
VACUUM
VACUUM scans a table, identifies dead row versions that no active transaction can still see, and marks the space they occupied as free for PostgreSQL to reuse for future rows. It runs concurrently with normal reads and writes — it does not block the table.
vacuum-basic.sql
VACUUM orders; -- Update planner statistics at the same time — very commonly run together VACUUM ANALYZE orders;
VACUUM FULL
VACUUM FULL is the version that actually shrinks the table on disk. It does this by rewriting the entire table into a new, compact file and then swapping it in — which means it needs an ACCESS EXCLUSIVE lock on the table for the duration of the operation.
ANALYZE
ANALYZE collects statistics about the distribution of values in each column — how many distinct values, roughly how rows are spread out — and stores them for the query planner to use. Accurate statistics are what let the planner choose good query plans (e.g. deciding whether an index scan or a sequential scan will be faster). Stale statistics after a table changes significantly can lead to poor plan choices even though nothing is functionally wrong with the data.
Autovacuum
Running VACUUM/ANALYZE manually on every table forever isn't practical, so PostgreSQL runs a background process called autovacuum that watches tables for enough changes (inserts, updates, deletes) and automatically vacuums and analyzes them without any manual intervention.
autovacuum_vacuum_scale_factor/autovacuum_vacuum_threshold— control how many dead rows accumulate before autovacuum kicks in.autovacuum_max_workers— how many autovacuum processes can run at once across the whole server.Per-table overrides are possible via
ALTER TABLE ... SET (autovacuum_vacuum_scale_factor = ...)for tables with unusual write patterns.
Monitoring Bloat
Bloat — the accumulation of dead tuples and unused space — can be checked via pg_stat_user_tables (columns like n_dead_tup show dead row counts) or dedicated bloat-estimation queries commonly shared in the PostgreSQL community. Keeping an eye on it helps catch tables where autovacuum isn't keeping up before it becomes a performance problem.