Tacnode™
Glossary
Data Consistency

What Is MVCC (Multi-Version Concurrency Control)?

Multi-Version Concurrency Control (MVCC) is a database concurrency-control method in which the system keeps multiple versions of each data item rather than overwriting it in place. Each transaction reads from a consistent snapshot assembled from committed versions, so readers never block writers and writers never block readers. MVCC is the concurrency foundation of PostgreSQL, MySQL's InnoDB engine, Oracle, and most modern transactional systems.

Updated

What is MVCC?

MVCC originates in David Reed's 1978 MIT dissertation and was formalized in concurrency-control theory by Bernstein and Goodman in the early 1980s. It addresses the central weakness of pure lock-based concurrency control: under two-phase locking, a long analytical read can block updates, and a stream of updates can starve readers. By letting every write create a new version instead of destroying the old one, the database can serve each reader the version that was current as of its snapshot — no read locks required.

Implementations differ in where old versions live. PostgreSQL stores every row version in the table heap itself, marking each tuple with the transaction IDs that created and deleted it (xmin/xmax); obsolete versions are reclaimed later by VACUUM. Oracle and InnoDB instead update rows in place and keep the information needed to reconstruct older versions in undo (rollback) segments. Both designs implement the same idea: visibility is a function of the reader's snapshot, not of locks.

MVCC is also the mechanism behind snapshot isolation, and PostgreSQL's SERIALIZABLE level (SSI) is built on top of it.

How MVCC works

A two-transaction timeline against one account row:

text
Time  Transaction A                     Transaction B
t1    BEGIN;
t2    SELECT balance FROM accounts
      WHERE id = 7;      -- reads 100
t3                                      BEGIN;
t4                                      UPDATE accounts SET balance = 30
                                        WHERE id = 7;  -- writes version 2
t5                                      COMMIT;
t6    SELECT balance FROM accounts
      WHERE id = 7;
      -- READ COMMITTED: reads 30  (fresh snapshot per statement)
      -- REPEATABLE READ: reads 100 (snapshot fixed for the transaction)
t7    COMMIT;

At t4, B does not wait for A even though A read the row, and A's t6 read does not wait for B's write — each transaction is served the version its snapshot permits. Two caveats keep this honest. First, MVCC does not eliminate write-write conflicts: if A also tried to update row 7, it would block until B committed and then, at stricter isolation levels, abort. Second, versions accumulate — PostgreSQL's VACUUM and InnoDB's purge exist to reclaim versions no active snapshot can still see, and a long-lived transaction pins old versions, causing table and undo bloat.

Why MVCC matters in real-time systems

MVCC is what makes it practical to run decision queries against a system that is being written to at high velocity. A query that scans thousands of rows gets one internally consistent picture — every row as of the same instant — without freezing ingestion. For workloads where correctness depends on reading coherent state under concurrent writes, that snapshot property is load-bearing; context under concurrency examines why.

The honest limits matter equally. A snapshot is consistent but not current: it reflects the moment it was taken, and a long-running read acts on progressively staler data as writes continue. And a snapshot's boundary is the single system that produced it — once context is assembled from a database plus caches, replicas, or stream-derived aggregates, there is no shared snapshot across them; each component reflects a different moment. MVCC solves reader-writer interference inside one system; it says nothing about coherence between systems.

FAQ

Related terms

Further reading