Snapshot Isolation: How Databases Freeze Time for Transactions


Modern databases handle dozens or thousands of concurrent transactions without grinding to a halt. One of the most elegant mechanisms enabling this is snapshot isolation, a concurrency control technique that gives each transaction its own consistent view of the database frozen at a specific point in time.

The Core Idea

When a transaction begins under snapshot isolation, it receives a snapshot of the committed database state as it existed at that moment. Throughout the transaction’s lifetime, it reads from this snapshot exclusively, seeing neither uncommitted changes from other transactions nor changes committed after its snapshot was taken.

This approach sidesteps many traditional locking problems. Read operations never block writes, and writes never block reads. A long-running analytics query can scan tables without interfering with OLTP workloads, and vice versa. The database maintains multiple versions of each row, allowing concurrent transactions to operate on different versions simultaneously.

Implementation Through MVCC

Snapshot isolation builds on multi-version concurrency control (MVCC). When a transaction modifies a row, the database doesn’t overwrite the existing version. Instead, it creates a new version tagged with transaction metadata—typically a transaction ID or timestamp. The old version remains accessible to transactions whose snapshots predate the change.

Each row version carries markers indicating which transaction created it and, if applicable, which transaction deleted or superseded it. When a transaction reads a row, the database walks the version chain to find the version visible to that transaction’s snapshot. Visibility rules are straightforward: a version is visible if it was committed before the snapshot began and hasn’t been deleted as of that snapshot.

Garbage collection runs periodically to purge old versions no longer needed by any active snapshot. This is critical for performance—without cleanup, version chains grow unbounded and reads degrade to linear scans through obsolete data.

Write Conflicts and First-Committer-Wins

While snapshot isolation eliminates read-write conflicts, write-write conflicts still require resolution. If two transactions concurrently modify the same row, the database typically employs a first-committer-wins policy. The first transaction to commit succeeds; the second receives a serialization error and must retry.

This detection happens at commit time. The database checks whether any rows the transaction modified have been updated by another transaction that committed after this transaction’s snapshot began. If so, allowing the commit would violate isolation guarantees, so the transaction aborts.

The Write Skew Anomaly

Snapshot isolation prevents dirty reads, non-repeatable reads, and phantom reads, but it doesn’t provide full serializability. The classic failure mode is write skew, where two transactions read overlapping data, make decisions based on what they read, and write to disjoint sets of rows in a way that violates a constraint.

Consider a hospital scheduling system ensuring at least one doctor remains on call. Two transactions might each read the on-call list, see two doctors available, and each release one doctor. Both transactions commit successfully because they modified different rows, but the constraint is now violated—no doctors remain on call.

True serializable isolation detects this by tracking read and write dependencies between transactions. PostgreSQL’s serializable snapshot isolation (SSI) and newer databases like CockroachDB implement this by maintaining predicate locks and detecting dangerous structures in the transaction dependency graph.

Performance Characteristics

Snapshot isolation’s read performance is excellent. Reads avoid locks entirely and proceed at native speed, limited only by the cost of version chain traversal. Short version chains keep this overhead minimal in well-tuned systems.

Write performance depends on version storage overhead and conflict rates. High-contention workloads—many transactions updating the same hot rows—experience frequent aborts and retries. Low-contention workloads with mostly disjoint write sets scale nearly linearly.

The mechanism also enables time-travel queries and point-in-time recovery. Since the database already maintains historical versions for concurrency control, exposing them for queries as of a past timestamp requires little additional machinery.

Why It Dominates

Snapshot isolation has become the default isolation level in many modern databases (often branded as “repeatable read”) because it offers a practical sweet spot. It provides strong enough guarantees for most applications while delivering the read scalability and low-latency characteristics that production systems demand. Understanding how your database freezes time for each transaction explains much about its performance profile and the anomalies you need to guard against in application logic.