Decision guide
Choosing a Transaction Isolation Level
Which isolation level should this transaction run at?
Side by side
| Criterion | Read Committed | Repeatable Read (snapshot) | Serializable |
|---|---|---|---|
| Prevents lost updates in read-modify-write | No (use atomic updates or locks) | Postgres: yes (error + retry); MySQL: no | Yes |
| Prevents write skew / phantoms | No | No | Yes |
| Consistent multi-statement reads | No | Yes | Yes |
| Needs application retries | Rarely (deadlocks only) | Postgres: on update conflicts | Yes, routinely |
| Throughput under contention | Highest | High | Lower (aborts or lock waits) |
When to choose each
Read Committed
Each statement sees data committed before it started. Postgres default.
Choose when
- Single-statement reads and writes
- Writes that use atomic updates, constraints or explicit locks
- High-concurrency OLTP where you control locking yourself
Avoid when
- Multi-statement reports that must add up
- Read-modify-write logic without locks or version checks
Repeatable Read (snapshot)
The transaction reads one consistent snapshot. MySQL InnoDB default; snapshot isolation in Postgres.
Choose when
- Multi-statement read-only reports and exports
- Postgres read-modify-write where you can retry serialization failures
Avoid when
- Rules spanning several rows (write skew still possible)
- Long-running jobs on the primary (holds back VACUUM / purge)
Serializable
Results equal some serial order. Postgres: SSI with aborts; MySQL: shared locks on reads.
Choose when
- Complex invariants across rows or tables (limits, rosters, quotas)
- You can retry the whole transaction safely
- Correctness matters more than peak throughput
Avoid when
- Very hot data where retries would snowball
- Transactions with side effects you can't repeat
Verdict
Default to Read Committed and make writes safe with atomic updates, constraints and explicit locks. Use Repeatable Read (read-only) for multi-statement reports. Reach for Serializable when an invariant spans several rows and every write path can be retried — and set the level explicitly in code that depends on it.
Learn more
- Transactions, Locking & Concurrency in Code
- Distributed Systems for Backend Engineers
- Databases & Transactions