Perfect.
Day 5 is one of the MOST important days — this is where many “it works in testing but fails in production” bugs come from.I’ll explain Isolation Levels in a way you can feel what the database is doing.
TRANSACTION ISOLATION LEVELS
Isolation = how much of other transactions your transaction is allowed to see
1️⃣ Why Isolation Levels Exist
Databases must balance:
- ✅ Correctness
- ⚡ Performance
- ? Concurrency
Higher isolation = safer but slower
Lower isolation = faster but risky
2️⃣ The 4 Isolation Levels (In Order)
| Level | Dirty Read | Non-repeatable Read | Phantom Read |
|---|---|---|---|
| READ UNCOMMITTED | ❌ | ❌ | ❌ |
| READ COMMITTED | ✅ | ❌ | ❌ |
| REPEATABLE READ | ✅ | ✅ | ❌ |
| SERIALIZABLE | ✅ | ✅ | ✅ |
(✅ = prevented)
3️⃣ The 3 Problems You Must Understand
? Dirty Read
Reading data that another transaction hasn’t committed
? Non-Repeatable Read
Reading the same row twice → different values
? Phantom Read
Same query → different rows appear/disappear
4️⃣ Isolation Levels Explained With Banking Examples
? READ UNCOMMITTED (Almost Never Used)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
What happens
- You can read uncommitted data
- Other transaction may rollback later
Banking example ❌
Txn A withdraws ₹10,000 (not committed)
Txn B sees reduced balance
Txn A rolls back
? Data lie
❌ Never used in finance
? READ COMMITTED (MOST COMMON)
@Transactional(isolation = Isolation.READ_COMMITTED)
Guarantees
- Only committed data is visible
- No dirty reads
Banking behavior
Txn A locks account
Txn B waits
Txn A commits
Txn B sees updated balance
✔ Safe for deposit/withdraw
✔ Good performance
✔ Used by banks
⚠ Limitation
Same row read twice can change.
? REPEATABLE READ
@Transactional(isolation = Isolation.REPEATABLE_READ)
Guarantees
- Same row read twice → same value
- Prevents lost updates (in many DBs)
Example
Read balance = 10,000
Another txn updates balance
Re-read → still 10,000
✔ Stronger consistency
❌ More locking / MVCC overhead
Used in:
- Reports
- Statements
- Long reads
? SERIALIZABLE (STRONGEST)
@Transactional(isolation = Isolation.SERIALIZABLE)
Behavior
- Transactions behave as if run one by one
- Prevents phantom reads
Example
SELECT count(*) FROM accounts WHERE balance > 10000
No new matching rows can appear during txn.
✔ Maximum safety
❌ Terrible performance
❌ Lock contention
❌ Rarely used in production
5️⃣ What Banks ACTUALLY Use
| Operation | Isolation |
|---|---|
| Deposit / Withdraw | READ_COMMITTED |
| Transfer | READ_COMMITTED + row lock |
| End-of-day report | REPEATABLE_READ |
| Audits | SERIALIZABLE (rare) |
6️⃣ Important Truth (This Will Save You)
Isolation alone does NOT prevent lost updates
You still need:
- Row-level locks (SELECT FOR UPDATE)
- Or atomic update queries
7️⃣ Correct Production Pattern (THIS IS GOLD)
✅ Write Transactions
@Transactional(isolation = Isolation.READ_COMMITTED)
public void withdraw(...) {
Account acc = repo.findForUpdate(id);
...
}
✅ Read Transactions
@Transactional(readOnly = true)
public Account getBalance(...) {
return repo.findById(id);
}
8️⃣ Why Not SERIALIZABLE Everywhere?
Because:
- DB throughput collapses
- Lock waits explode
- Latency spikes
- Deadlocks increase
Banks prefer:
Minimal isolation + correct locking
9️⃣ One-Line Mental Models (Memorize)
- READ COMMITTED → “See only finished work”
- REPEATABLE READ → “Rows don’t change”
- SERIALIZABLE → “World is frozen”
? Day 5 Final Summary
✔ Isolation controls visibility
✔ Locks control correctness
✔ READ_COMMITTED + row locks = sweet spot
✔ Higher isolation ≠ better system
? Where You Are Now
You understand:
- Transactions
- Idempotency
- Deadlocks
- Contention
- Isolation levels
This is core backend engineering knowledge.
Optimistic Locking vs Pessimistic Locking — When to Use What