MVCC & Isolation Internals

Difficulty
Importance

The problem

Enforcing transaction isolation by simply locking every row a transaction reads would serialize huge amounts of otherwise-unrelated work — readers would constantly block writers and vice versa — even though many of those conflicts aren't real, just accidental timing overlaps.

Why now

Transactions and ACID already named isolation as a guarantee without explaining how it's achieved cheaply; mutability and references already showed that keeping an old, unmodified copy of a value alongside a new one avoids an entire class of aliasing conflicts — MVCC is exactly that idea, applied to entire database rows instead of a single in-memory reference.

Mental model

Instead of one mutable row that readers and writers fight over, MVCC keeps multiple versions of each row, tagged with when they were created and (if superseded) when they died — a reader is simply handed whichever version was current at the moment its transaction started, so readers never block writers and writers never block readers, because nobody is actually sharing a single mutable copy in the first place.

Requires

Used in

Projects

  • Start a long-running read transaction, then perform and commit an update to the same row from a second connection, and observe that the first transaction still sees its original snapshot
  • Explain, using a diagram, how a database decides which version of a row a given transaction should see based on transaction start times

Examples

  • A transaction that started before a concurrent UPDATE committed keeps seeing the pre-update row version until it commits or rolls back — snapshot isolation, not blocking
  • PostgreSQL's VACUUM process reclaims disk space from row versions no longer visible to any active transaction — the cleanup cost MVCC defers in exchange for lock-free reads

Resources

Mastery checklist