Interview Q&A / PostgreSQL

IntermediateIn-depth3 min readUpdated 2 Oct 2026

What do VACUUM and autovacuum do in PostgreSQL?

SHORT ANSWER

Because of MVCC, updates and deletes leave dead row versions behind. VACUUM marks that space as reusable, updates the visibility map and freezes old transaction IDs so they cannot wrap around. Autovacuum is a background process that runs VACUUM and ANALYZE automatically when enough rows have changed.

Explain it simply

Think of a hostel mess register. When a student changes their meal plan, the clerk does not erase the old line; they strike it out and write a new line below. After a few months the register is full of struck-out lines. Someone has to go through it and mark those lines as free space for new entries. That is VACUUM.

In PostgreSQL every UPDATE writes a new version of the row and leaves the old one behind, and every DELETE only marks a row as gone. These leftovers are called dead tuples. Nobody can see them any more, but they still take space until VACUUM cleans them up.

Example

sql
-- How many dead rows does each table have, and when was it last vacuumed?
SELECT relname, n_live_tup, n_dead_tup, last_autovacuum, last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 5;

-- Run it by hand on one table, and refresh planner statistics at the same time
VACUUM (VERBOSE, ANALYZE) orders;

Sample result of the first query:

relnamen_live_tupn_dead_tuplast_autovacuum
orders1200000950002026-10-02 09:14
sessions40000310000(null)

Here sessions has far more dead rows than live ones and has never been auto-vacuumed. That is worth investigating.

How it works

A plain VACUUM does three main jobs:

  1. Reclaims dead tuples: it removes dead row versions and their index entries, and records the free space so new rows can reuse it. It usually does not shrink the file on disk, except for empty pages at the very end.
  2. Updates the visibility map: pages where every row is visible to everyone are marked, which lets index-only scans skip reading the table.
  3. Freezes old rows: transaction IDs are 32-bit and eventually wrap around. VACUUM marks very old rows as "frozen" so they stay visible forever. If this never happens, PostgreSQL will eventually refuse new writes to protect data.

Plain VACUUM runs alongside normal reads and writes. VACUUM FULL is different: it rewrites the whole table into a new file and gives space back to the operating system, but it takes an exclusive lock that blocks all reads and writes for the duration.

Autovacuum checks tables regularly. By default it vacuums a table when dead tuples exceed 50 rows plus 20% of the table, and analyzes it when changes exceed 50 rows plus 10%. These are the settings autovacuum_vacuum_threshold, autovacuum_vacuum_scale_factor and their analyze versions.

Real-world use

  • A session or cart table with constant updates and deletes needs autovacuum to keep up, or it bloats and slows down.
  • On very large tables, 20% is too many rows to wait for, so teams lower the scale factor per table: ALTER TABLE orders SET (autovacuum_vacuum_scale_factor = 0.02);.
  • After a bulk load, running ANALYZE (or VACUUM ANALYZE) gives the planner fresh statistics.

Common mistakes

  • Turning autovacuum off because it "uses resources". Bloat and wraparound risk are far worse.
  • Running VACUUM FULL on a busy production table during the day. It locks the table completely.
  • Ignoring long-running or "idle in transaction" sessions. VACUUM cannot remove rows that an open snapshot might still need, so cleanup stalls.
  • Confusing VACUUM with ANALYZE. VACUUM cleans space; ANALYZE collects statistics for the planner.

What to say in the interview

"Because PostgreSQL uses MVCC, updates and deletes leave dead row versions. VACUUM removes them and makes the space reusable, updates the visibility map for index-only scans, and freezes old transaction IDs to prevent wraparound. Plain VACUUM does not block reads or writes. VACUUM FULL rewrites the table and returns space to the OS but locks it. Autovacuum runs VACUUM and ANALYZE automatically based on how many rows changed, and on big or busy tables I tune its thresholds per table rather than turning it off."

Follow-up questions

  • What is transaction ID wraparound?
  • Why can a long-running transaction cause bloat?
  • How would you reclaim disk space from a bloated table without long locks?

Go deeper

Read these once the basics above feel easy.

To shrink a bloated table without blocking it for long, teams use the pg_repack extension, which rebuilds the table in the background and only takes a short lock at the end. To watch wraparound risk, check age(datfrozenxid) in pg_database; autovacuum forces an aggressive anti-wraparound vacuum when a table's age passes autovacuum_freeze_max_age (200 million transactions by default), even if autovacuum is otherwise disabled for that table.

#postgresql#vacuum#autovacuum#maintenance#mvcc

Related questions

Practise more PostgreSQL questions

Test your recall with short answers, then return to the detailed explanations when you need context.

Open PostgreSQL quick answers →

Get new questions like this by email

One short email when something new and useful lands. No spam, unsubscribe in one tap.

By subscribing you agree to the Privacy Policy.