MVCC and Snapshot Isolation: High Concurrency with Trade-offs
How MVCC Works
Multi-Version Concurrency Control (MVCC) is the dominant strategy in modern databases. Instead of locking rows, writers create new versions tagged with commit timestamps. Readers select a snapshot timestamp and see the latest version committed before that time. This decouples readers from writers: readers never block writers and writers never block readers, eliminating a major source of contention. Long-running analytical queries can read consistent snapshots without interfering with transactional writes happening simultaneously.
Performance Benefits
For read-heavy workloads, MVCC is dramatically faster than lock-based isolation. Enabling snapshot isolation on systems handling 10,000-100,000 transactions/sec typically eliminates read/write blocking and reduces deadlock rates to near zero. Reporting queries running 10-30 minutes execute against stable snapshots while transactional writes proceed unimpeded. The trade-off: version storage overhead and write skew risk.
Snapshot Isolation vs Serializable
Snapshot Isolation (SI) provides strong consistency for most workloads: each transaction sees a consistent snapshot at start time. It prevents dirty reads, non-repeatable reads, and phantoms. However, SI does not prevent write skew because each transaction reads a valid snapshot and makes locally-valid writes that together may violate invariants. Serializable Snapshot Isolation (SSI) extends SI by tracking which rows each transaction reads and writes. If the database detects that two concurrent transactions have conflicting dependencies (one wrote data the other read), it aborts one to maintain serializability.
Version Storage Overhead
Old versions must be retained until no active transaction needs them. A background process called version cleanup (or vacuum) periodically removes obsolete versions. Different databases handle storage differently: some store old versions in the main table causing bloat, others use separate version stores. Long-running transactions prevent cleanup from reclaiming versions, inflating storage by gigabytes and degrading write throughput. Monitor active transaction duration and set timeouts.
Read Committed vs Snapshot Isolation
Read Committed uses a new snapshot for each statement. Snapshot Isolation uses a single snapshot for the entire transaction. Read Committed is cheaper (no version retention across statements) but allows non-repeatable reads. Choose Read Committed for simple transactional workloads where each statement is independent. Choose Snapshot Isolation when transaction logic depends on consistent reads across multiple statements, such as calculating a total then updating based on it.