Skip to main content
KBY Technologies Logo
cd ../lexicon
sys/docs/lexicon/mvcc-multi-version-concurrency-control.md
Lexicon Entry

MVCC (Multi-Version Concurrency Control)

MVCC is a concurrency control technique that keeps multiple timestamped versions of each data item, letting readers see a consistent snapshot without blocking or being blocked by concurrent writers.

Practical example

In PostgreSQL, an UPDATE creates a new tuple version and marks the old one as expired; VACUUM later reclaims space once no active snapshot still needs the old version. In CockroachDB, a follower replica can serve a read at a given timestamp without contacting the leader, provided it has received all writes committed before that timestamp — a direct application of MVCC's snapshot semantics to distributed reads.

MVCC decouples readers from writers by never mutating a record in place. Instead, every write creates a new version tagged with a monotonically increasing identifier — typically a transaction ID, a commit timestamp, or an HLC value in geo-distributed systems. A read operation is bound to a snapshot (a specific timestamp or transaction epoch) and simply scans backward through the version chain until it finds the newest version whose commit timestamp is <= the snapshot’s timestamp. This is why MVCC readers never acquire row-level locks: they are reading immutable history, not contending for the current mutable state.

The architectural cost is version bloat. Every UPDATE or DELETE is logically an INSERT of a new version plus a marker on the old one (conceptually a tombstone, though the garbage collection mechanics differ from distributed tombstone propagation). Systems must run a reclamation process — Postgres calls it VACUUM, CockroachDB and Spanner run a GC threshold sweep tied to the oldest active read timestamp. If a long-running transaction or an analytical query holds a snapshot open for hours, the GC horizon cannot advance, and dead versions accumulate unbounded. This is the operational failure mode every MVCC operator dreads: table bloat, index bloat, and eventually transaction ID wraparound (in systems using 32-bit XIDs) or storage exhaustion.

MVCC also redefines what anomalies are possible. It eliminates dirty reads and non-repeatable reads by construction, but write skew remains possible under Snapshot Isolation because two transactions can each read a consistent-but-stale snapshot, make disjoint writes that individually look valid, and commit concurrently — violating an invariant that spans both rows. Serializable Snapshot Isolation (SSI), used by Postgres and CockroachDB, closes this gap by tracking read-write dependency graphs at commit time and aborting one transaction if a dangerous rw-antidependency cycle is detected, rather than by locking.

In distributed deployments, MVCC’s snapshot semantics become the mechanism for achieving stale-read replicas and bounded staleness: a follower can safely serve reads at any timestamp for which it has received all committed versions, without coordinating with the leader, as long as it can prove no earlier-committed write is still in flight. This is precisely how Spanner’s TrueTime-bound snapshot reads and CockroachDB’s follower reads work — MVCC timestamps become the currency exchanged between the consistency protocol and the storage engine, turning what is fundamentally a local storage technique into a building block for multi-region read scalability.