MVCC and VACUUM
See PostgreSQL MVCC through xmin, xmax, and ctid — then watch dead tuples accumulate and learn to reclaim that space with VACUUM and VACUUM FULL.
Every UPDATE and DELETE in PostgreSQL leaves behind a dead row version — an invisible ghost that occupies real disk space until VACUUM arrives. This course makes that behaviour tangible. You will query the hidden system columns xmin, xmax, and ctid directly, watch ctid change position after an UPDATE, see xmax filled in after a DELETE, and measure dead tuple counts rising in pg_stat_user_tables. Then you will run VACUUM and VACUUM FULL, compare the before-and-after sizes, and read the VERBOSE output line by line. By the end you will understand why PostgreSQL tables grow, when regular VACUUM is enough, and when VACUUM FULL is necessary — and what it costs.
What you'll build
- Read xmin, xmax, and ctid from a live table and explain what each value means
- Observe how an UPDATE creates a new row version and leaves the old one as a dead tuple
- Track n_dead_tup rising in pg_stat_user_tables after repeated writes
- Run VACUUM and verify dead tuples are removed without shrinking the file
- Interpret VACUUM VERBOSE output to understand what the engine actually cleaned
- Run VACUUM FULL and confirm the file size returned to the OS
- Choose between VACUUM and VACUUM FULL based on locking requirements and bloat severity
Contents
- Isolated PostgreSQL Stand
- Start it
- Connect with psql
- Accounts and Orders Tables
- Events and Profiles Tables
- Seeding Accounts
- Seeding Orders
- Seeding Events
- Seeding Profiles
- Creating the Tables
- Loading Seed Data
- Single-Threaded Plans and Fresh Statistics
- Confirming the PostgreSQL Version
- Hidden System Columns
- Reading the System Columns
- UPDATE Writes a New Row Version
- The Row Moved
- A Second Dead Version
- Preparing a Row for Deletion
- DELETE Makes the Row Invisible, Not Gone
- Monitoring Table Health
- Clean Starting Point
- Simulating Write Traffic
- First Churn Run
- Dead Tuples Appear
- Scaling Up the Churn
- Two Thousand Dead Tuples
- Measuring the Bloat
- What VACUUM Does
- Running VACUUM
- Dead Tuples Gone
- File Size Unchanged After VACUUM
- Reading the VACUUM Report
- Rebuilding the Dead Tuple Backlog
- Interpreting the Verbose Report
- Append-Only vs Write-Heavy Tables
- A Separate Churn Script for Profiles
- Churning the Profiles Table
- Profiles Is Now Bloated
- File Size Reflects the Bloat
- VACUUM FULL Rewrites the Table
- The Table Was Rewritten
- Space Returned to the OS
- Four Tables, Three Stories
- Ranking Tables by Size
- Size in Perspective
- Long Transactions Block VACUUM
- Back to the Beginning
- Healthy Rows Look Like This
- All Tables Clean
- Tearing Down the Stand
- Production Monitoring Cheatsheet