Serializability
A guarantee that concurrent transactions produce the same result as if they were executed one at a time in some serial order. The gold standard for database transaction isolation.
What is Serializability?
In short
Serializability is the database correctness guarantee that a set of concurrent transactions produces exactly the same result as if those transactions had run one after another, in some serial order, with no overlap. It is the strongest isolation level in the SQL standard, and any outcome a serializable system produces is one you could have gotten by running the transactions in a single-file line.
What serializability actually guarantees
When many transactions read and write the same rows at the same time, their operations interleave. Serializability is the promise that whatever interleaving the database chooses to run, the final state and the values every transaction reads are equivalent to some serial order, meaning one transaction fully finishes before the next one starts. You do not get to pick which order. You only get the guarantee that one valid order existed.
This matters because it lets you reason about a transaction in isolation. You can write the logic assuming nothing else touches your data while you run, and the database makes that assumption true. Bugs that only show up under concurrency, like two transactions both reading a balance of 100 and both subtracting 60 to leave 40 instead of negative 20, simply cannot happen under serializability.
Serializability is not the same as a specific real-time order. If transaction A and transaction B run at the same moment, serializability allows the result to match either A then B, or B then A, whichever the engine ended up with. The stronger guarantee that also respects wall-clock ordering is called strict serializability or linearizability for single objects, and systems like Google Spanner provide it.
How databases enforce it under the hood
The classic mechanism is two-phase locking (2PL). A transaction acquires shared locks to read and exclusive locks to write, holds every lock until it commits, then releases them all at once. Because no other transaction can touch a locked row, the lock order forces a valid serial order. The cost is contention and deadlocks, which the engine detects and resolves by aborting one of the stuck transactions.
The modern alternative is Serializable Snapshot Isolation (SSI), used by PostgreSQL since version 9.1. Each transaction reads from a consistent snapshot without taking read locks, so reads never block writes. At commit time the engine checks for dangerous read-write dependency cycles that could break serializability, and if it finds one it aborts a transaction with a serialization failure. This trades blocking for occasional retries, which is why application code talking to a SERIALIZABLE Postgres must be ready to catch error 40001 and retry.
A third family is multi-version timestamp ordering, where each transaction gets a timestamp and writes create new row versions. Conflicts that violate the timestamp order force an abort. The shared idea across all three is the same: detect or prevent any interleaving that no serial order could have produced, and reject the transactions that would create one.
When to use it and what it costs
Use serializability when correctness under concurrency is more important than raw throughput: moving money between accounts, decrementing inventory so you never oversell, enforcing uniqueness or quota rules that a check-then-write pattern would otherwise break. These are exactly the cases where weaker levels like Read Committed silently allow anomalies such as write skew, where two transactions each read a shared condition, both decide they are allowed to proceed, and together violate an invariant neither broke alone.
The cost is performance and aborts. Lock-based serializability creates contention and can deadlock under heavy write overlap. SSI avoids the locks but raises the abort and retry rate as conflict rates climb, and every retry burns work you already did. Under high contention on a few hot rows, serializable throughput can fall well below what a weaker level delivers.
Most production systems do not run everything at SERIALIZABLE. A common pattern is to default to Read Committed or Snapshot Isolation for ordinary reads and reporting, and reserve SERIALIZABLE for the handful of transactions where a concurrency anomaly would corrupt money or counts. The discipline is choosing the weakest isolation that is still correct for each operation, not blindly maxing it out everywhere.
A concrete example
Imagine a banking app with two accounts and a rule that their combined balance must stay at or above zero. Each account holds 100, so the pair holds 200. Transaction A reads both balances, sees 200 total, and withdraws 150 from account one. At the same time transaction B reads both balances, also sees 200, and withdraws 150 from account two. Each transaction checked the rule and believed it was safe.
Under a weaker isolation level both can commit, leaving account one at minus 50 and account two at minus 50, a combined minus 100 that violates the invariant. This is write skew, and it is invisible to each transaction because neither one wrote a row the other read. Under serializability the database recognizes that no serial order of A and B could produce this outcome, so it aborts one of them. The application catches the serialization failure, retries, and now reads the already-reduced balance and correctly refuses the second withdrawal.
PostgreSQL handles exactly this case with SSI. The key engineering takeaway is that serializability moves the burden of getting concurrency right from your application code into the database, in exchange for accepting that some transactions will be told to try again.
Where it is used in production
PostgreSQL
Implements SERIALIZABLE using Serializable Snapshot Isolation, which detects read-write dependency cycles at commit and aborts offending transactions with error 40001.
Google Spanner
Provides strict serializability across a global database, using TrueTime atomic-clock timestamps so the serial order also respects real-world commit order.
CockroachDB
Runs every transaction at serializable isolation by default, using multi-version concurrency control plus timestamp ordering and retries on conflict.
FoundationDB
Offers strict serializability for all transactions, validating read-write conflicts at commit and aborting any transaction whose reads were invalidated.
Frequently asked questions
- What is the difference between serializability and linearizability?
- Serializability is about transactions and says the result equals some serial order of those transactions, but it does not promise that order matches real time. Linearizability is about single operations on a single object and says each one appears to take effect at one instant between its start and finish, respecting real-time order. The combination of both, applied to transactions, is called strict serializability and is what Google Spanner provides.
- Is serializable isolation the same as the SERIALIZABLE level in SQL?
- Yes, SERIALIZABLE is the strongest of the four isolation levels in the SQL standard, and a correct implementation prevents all of dirty reads, non-repeatable reads, phantom reads, and write skew. Be careful though, because some databases historically labeled Snapshot Isolation as SERIALIZABLE, which still allows write skew. PostgreSQL only became truly serializable in version 9.1 with SSI.
- Why does serializability cause transactions to fail and retry?
- Modern serializable engines like PostgreSQL SSI and CockroachDB do not block reads with locks. Instead they let transactions run on snapshots and check for conflicting dependencies at commit. If committing a transaction would create an interleaving no serial order could produce, the engine aborts it with a serialization failure so your application must catch the error and retry the transaction.
- What anomaly does serializability prevent that Snapshot Isolation does not?
- Write skew. Two transactions each read an overlapping set of rows, each makes a decision based on what it read, and each writes a different row, so neither sees the other's write. Together they break an invariant that held for each one individually, such as a combined-balance or scheduling rule. Snapshot Isolation allows this, while full serializability detects and rejects it.
- Should I run my whole database at serializable isolation?
- Usually not. Serializable isolation adds contention, deadlocks, or retries that lower throughput under heavy write conflict. The common practice is to default to Read Committed or Snapshot Isolation for ordinary reads and reporting, and reserve SERIALIZABLE for the specific transactions where a concurrency anomaly would corrupt money, inventory counts, or other hard invariants.
Learn Serializability hands-on
This page explains the idea. The full lesson lets you step through the ring as servers join and leave, read the implementation, and check yourself with a quiz. It is one of 760+ lessons in the System Design Masterclass, from your first API call to distributed consensus. Eleven Foundation lessons are free, no signup. Lifetime access is ₹499 in India or $7.99 worldwide, one payment, no subscription.
See also
Related glossary terms you might want to look up next.
Linearizability
The strongest consistency guarantee: every operation appears to take effect atomically at some point between its invocation and completion. As if there's a single copy of the data.
Isolation Level
Controls how much one transaction can see changes made by other concurrent transactions. Ranges from Read Uncommitted (fastest, least safe) to Serializable (slowest, safest).
ACID
Four guarantees for database transactions: Atomicity (all or nothing), Consistency (valid states only), Isolation (no interference), Durability (changes persist).
Eventual Consistency
A consistency model where updates propagate asynchronously and all replicas will eventually converge to the same value. Trades immediacy for availability.
Strong Consistency
A guarantee that after a write completes, all subsequent reads will return the updated value. Safer but slower than eventual consistency.
Snapshot Isolation
A consistency level where each transaction reads from a consistent snapshot of the database taken at the transaction's start time. Prevents dirty reads without full serializability overhead.