Isolation Levels & Deadlocks
Isolation Levels & Deadlocks
Section titled “Isolation Levels & Deadlocks”Isolation levels control how much transactions can see each other’s changes. Deadlocks happen when two transactions wait on each other’s locks.
Real-World Analogy
Section titled “Real-World Analogy”Imagine a shared whiteboard in an office:
| Level | What You Can See | Problem |
|---|---|---|
| Read Uncommitted | See someone’s rough sketches before they’re final | They might erase it! |
| Read Committed | Only see final drawings | Someone might update it later |
| Repeatable Read | Everything looks the same throughout your meeting | New items might appear |
| Serializable | One person at a time — no surprises! | Everyone must wait their turn |
The 4 Isolation Levels
Section titled “The 4 Isolation Levels”flowchart TB subgraph Levels[Isolation Levels — Stronger → Slower] RU["READ UNCOMMITTED<br/>❌ Dirty Read<br/>❌ Non-Repeatable Read<br/>❌ Phantom Read"] RC["READ COMMITTED<br/>✅ No Dirty Read<br/>❌ Non-Repeatable Read<br/>❌ Phantom Read"] RR["REPEATABLE READ (MySQL Default)<br/>✅ No Dirty Read<br/>✅ No Non-Repeatable Read<br/>❌ Phantom Read"] S["SERIALIZABLE<br/>✅ No Dirty Read<br/>✅ No Non-Repeatable Read<br/>✅ No Phantom Read"] end
RU -->|More Protection| RC RC -->|More Protection| RR RR -->|More Protection| S
style RU fill:#ef4444,color:#fff style RC fill:#f59e0b,color:#fff style RR fill:#3b82f6,color:#fff style S fill:#7c3aed,color:#fffWhat the Anomalies Look Like
Section titled “What the Anomalies Look Like”Dirty Read — Reading Uncommitted Data:
Transaction A: Transaction B:1. UPDATE salary = 900002. SELECT salary → 90000 (reads uncommitted!)3. ROLLBACK (salary back to 50000) → B used data that never existed! ❌Non-Repeatable Read — Same Row, Different Values:
Transaction A: Transaction B:1. SELECT salary → 500002. UPDATE salary = 900003. COMMIT4. SELECT salary → 90000 (different from step 1!) → Inconsistent read within same transaction ❌Phantom Read — New Rows Appear:
Transaction A: Transaction B:1. SELECT * FROM employees WHERE dept = 'IT' → 5 rows2. INSERT new employee in IT3. COMMIT4. SELECT * FROM employees WHERE dept = 'IT' → 6 rows (phantom!) → New row appeared! ❌Isolation Level Comparison
Section titled “Isolation Level Comparison”| Level | Dirty Read | Non-Repeatable Read | Phantom Read | Performance |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ Possible | ✅ Possible | ✅ Possible | ⚡ Fastest |
| READ COMMITTED | ❌ Prevented | ✅ Possible | ✅ Possible | ⚡ Fast |
| REPEATABLE READ (default) | ❌ Prevented | ❌ Prevented | ✅ Possible | 🐢 Slower |
| SERIALIZABLE | ❌ Prevented | ❌ Prevented | ❌ Prevented | 🐌 Slowest |
-- Set isolation level for a sessionSET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Set globally (careful!)SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Check current isolation levelSELECT @@transaction_isolation;-- MySQL default: REPEATABLE READDeadlocks
Section titled “Deadlocks”A deadlock occurs when two transactions each hold a lock the other needs.
The Deadlock Scenario:
sequenceDiagram participant TxA as Transaction A participant DB as MySQL participant TxB as Transaction B
TxA->>DB: LOCK row 1 ✅ (has lock) TxB->>DB: LOCK row 2 ✅ (has lock)
TxA->>DB: Request LOCK row 2 ⏳ (waiting for B) TxB->>DB: Request LOCK row 1 ⏳ (waiting for A)
Note over DB: 🔥 DEADLOCK DETECTED! DB->>TxA: ROLLBACK (undoes changes) DB->>TxB: ✅ Committed successfully-- Transaction A:START TRANSACTION;UPDATE accounts SET balance = balance - 500 WHERE id = 1; -- locks row 1-- ... some work ...UPDATE accounts SET balance = balance + 500 WHERE id = 2; -- waits for row 2COMMIT;
-- Transaction B (running at the same time):START TRANSACTION;UPDATE accounts SET balance = balance - 300 WHERE id = 2; -- locks row 2-- ... some work ...UPDATE accounts SET balance = balance + 300 WHERE id = 1; -- waits for row 1COMMIT;
-- Result: DEADLOCK 🚫-- MySQL automatically detects and rolls back the transaction that did less workHow MySQL Handles Deadlocks
Section titled “How MySQL Handles Deadlocks”-- Check for deadlock informationSHOW ENGINE INNODB STATUS;-- Look for: "LATEST DETECTED DEADLOCK" section
-- MySQL's response:-- 1. Detects deadlock automatically (every ~1ms)-- 2. Chooses the "victim" (transaction with less work done)-- 3. Rolls back the victim transaction-- 4. Returns error: "Deadlock found when trying to get lock"-- 5. The other transaction completes normallyPreventing Deadlocks
Section titled “Preventing Deadlocks”✅ Always lock tables in the SAME ORDER: ❌ Bad: TxA locks A then B, TxB locks B then A ✅ Good: Both lock A then B
✅ Keep transactions SHORT: ❌ Bad: Start transaction, go make coffee, then commit ✅ Good: Start → work → commit immediately
✅ Use appropriate isolation level: READ COMMITTED has fewer locks than REPEATABLE READ
✅ Use indexes: Without indexes, MySQL locks more rows than necessary
✅ Avoid user interaction inside transactions: Don't prompt for input — that keeps locks open!
// Example of safe locking order:START TRANSACTION;-- Always lock accounts in ID orderUPDATE accounts SET balance = balance - 500 WHERE id = 1;UPDATE accounts SET balance = balance + 500 WHERE id = 2;COMMIT;Error Handling for Deadlocks
Section titled “Error Handling for Deadlocks”# Application-level retry logicmax_retries = 3for attempt in range(max_retries): try: cursor.execute("START TRANSACTION") cursor.execute("UPDATE accounts ...") cursor.execute("UPDATE accounts ...") connection.commit() break # success! except MySQLdb.Error as e: if e.args[0] == 1213: # Deadlock error code connection.rollback() time.sleep(0.5 * (attempt + 1)) # wait before retry continue else: raiseIn Simple Words
Section titled “In Simple Words”- Isolation levels control how transactions interfere with each other — stronger = slower but safer
- READ UNCOMMITTED = no protection; READ COMMITTED = no dirty reads; REPEATABLE READ (default) = consistent reads; SERIALIZABLE = full protection, slowest
- A deadlock is two transactions waiting on each other’s locks — MySQL detects and kills one
- Prevent deadlocks by locking resources in the same order, keeping transactions short, and using indexes
- Always implement retry logic in your application for deadlock errors