~/notes/mvcc

MVCC

Multi-Version Concurrency Control serves row versions according to transaction visibility rules.

Jul 11, 2026

Multi-Version Concurrency Control (MVCC) keeps information needed to present older logical versions of rows. A reader selects the version visible to its snapshot instead of necessarily waiting for a concurrent writer to finish.

MVCC can improve reader-writer concurrency, but it does not mean that all operations are lock-free. Writers can conflict with writers, explicit locking reads acquire locks, schema changes have separate rules, and engines must eventually reclaim obsolete versions.

Visibility

A version is visible according to engine-specific transaction metadata and snapshot rules. The scope of a snapshot depends on the isolation level.

In PostgreSQL:

  • Read Committed: each statement sees a snapshot as of the start of that statement.
  • Repeatable Read: statements use the snapshot established by the transaction’s first non-control statement.
  • Serializable: uses Serializable Snapshot Isolation and can abort a transaction when concurrent behavior cannot be serialized.

In InnoDB:

  • ordinary consistent reads use multi-versioning;
  • at Repeatable Read, consistent reads normally share the snapshot established by the first such read;
  • at Read Committed, each consistent read obtains a fresh snapshot;
  • locking reads and data-modification statements follow different locking and visibility rules.

The same isolation-level name can therefore differ across databases.

Version Storage And Cleanup

PostgreSQL stores tuple versions in table storage and uses visibility metadata to decide which tuple a snapshot can see. Vacuum later reclaims versions that no active transaction can require.

InnoDB reconstructs older versions using records in undo logs. Purge removes history that is no longer needed.

Long-running transactions retain old snapshots and can delay cleanup, increasing storage use and maintenance work. Monitor transaction age, not only query duration.

Correctness

A stable snapshot is not necessarily a serial execution. Snapshot isolation can permit write skew when two transactions read the same invariant and update different rows. Use serializable isolation, constraints, or deliberate locking when the invariant requires it.

Further Reading