Lab 09: PostgreSQL WAL & MVCC

Time: 45 minutes | Level: Advanced | DB: PostgreSQL 15

Overview

PostgreSQL's WAL (Write-Ahead Log) ensures durability and enables replication. MVCC (Multi-Version Concurrency Control) allows readers never to block writers and vice versa — by keeping multiple row versions. This lab explores both mechanisms and their operational implications.


Step 1: WAL Configuration and pg_wal Directory

docker run -d --name pg-lab \
  -e POSTGRES_PASSWORD=rootpass \
  postgres:15 \
  -c wal_level=replica \
  -c max_wal_size=1GB \
  -c min_wal_size=80MB \
  -c checkpoint_completion_target=0.9 \
  -c wal_compression=on

sleep 12

# Check WAL directory
docker exec pg-lab ls -la /var/lib/postgresql/data/pg_wal/ | head -10
echo ""

# Check WAL settings
docker exec pg-lab psql -U postgres -c "
SELECT name, setting, unit
FROM pg_settings
WHERE name IN (
  'wal_level', 'max_wal_size', 'min_wal_size',
  'checkpoint_completion_target', 'wal_compression',
  'checkpoint_timeout', 'wal_segment_size'
);
"

📸 Verified Output:

💡 Each WAL file is 16MB by default. max_wal_size controls how much WAL to keep before forcing a checkpoint. Higher values = less frequent checkpoints = better write throughput but longer crash recovery.


Step 2: WAL Functions — Track WAL Position

📸 Verified Output:


Step 3: MVCC — xmin and xmax System Columns

📸 Verified Output:

💡 ctid = (page, tuple) = physical location of this row version. xmin=739 = transaction 739 created these rows. xmax=0 = not deleted/updated (still the live version).


Step 4: Observe MVCC with Concurrent Updates

📸 Verified Output:


Step 5: VACUUM and AUTOVACUUM

📸 Verified Output:

💡 Regular VACUUM reclaims dead tuple space for reuse (fast, no lock). VACUUM FULL rewrites the table and shrinks the file (slow, exclusive lock). Autovacuum handles regular VACUUM automatically — tune autovacuum_vacuum_scale_factor for high-churn tables.


Step 6: EXPLAIN Shows Heap Fetches (MVCC Cost)

📸 Verified Output:


Step 7: Transaction ID Wraparound Protection

📸 Verified Output:


Step 8: Capstone — WAL + MVCC Health Check

📸 Verified Output:


Summary

Concept
Location/Tool
Key Detail

WAL files

/var/lib/postgresql/data/pg_wal/

16MB each, named by LSN

WAL functions

pg_walfile_name(), pg_current_wal_lsn()

Track WAL position

xmin

System column

XID that created this row version

xmax

System column

XID that deleted/updated this row version (0=live)

ctid

System column

Physical location (page, tuple)

Dead tuples

pg_stat_user_tables.n_dead_tup

Old row versions not yet reclaimed

VACUUM

VACUUM tablename

Reclaim dead tuple space (no lock)

VACUUM FULL

VACUUM FULL tablename

Rewrite table, shrink file (exclusive lock)

Autovacuum

Background daemon

Automatic VACUUM to manage dead tuples

XID wraparound

age(datfrozenxid)

32-bit XID wraps at 2 billion — monitor!

Key Takeaways

  • MVCC never blocks readers — writers create new versions, readers see old ones

  • Dead tuples accumulate after UPDATE/DELETE — autovacuum reclaims them

  • Index-only scans need VACUUM to update visibility map — run VACUUM regularly

  • XID wraparound at 2 billion transactions causes data loss — autovacuum prevents it

  • Monitor n_dead_tup / n_live_tup ratio; >20% = tune autovacuum aggressiveness

Last updated