TL;DR | 面試情境模擬 #
👴 面試官:資料庫的 Transaction 隔離級別有哪些?
🧑💻 你:有四個,從寬鬆到嚴格:Read Uncommitted(可以讀到未提交的資料)、Read Committed(只能讀已提交的,防止 Dirty Read)、Repeatable Read(同一 Transaction 多次讀結果一致,防止 Non-repeatable Read,是 MySQL 預設)、Serializable(完全串行,防止 Phantom Read,但效能最差)。大多數系統用 Read Committed 或 Repeatable Read 在一致性和效能間取平衡。
比喻:考試閱卷 #
想像多個考生同時修改考卷(Transaction),閱卷老師在不同時機看到不同版本:
- Read Uncommitted:考生還在改,老師就去看了 → 看到草稿版
- Read Committed:考生交卷後才看 → 看到正式版,但考生可以改多次,每次看都不同
- Repeatable Read:老師決定看的那一刻起,看到的版本就鎖定了,考生後來怎麼改都不影響
- Serializable:整個考場只能同時有一個人在改,完全串行,沒有任何干擾
三種並發問題 #
Dirty Read 髒讀 #
讀到另一個 Transaction 還沒提交(可能會 Rollback)的資料。
T1:UPDATE balance = 500(尚未 COMMIT)
T2:SELECT balance → 讀到 500
T1:ROLLBACK(實際上 balance 還是 200)
T2 讀到的 500 是假的
Non-repeatable Read 不可重複讀 #
同一 Transaction 裡,兩次讀同一筆資料,結果不同(因為中間有別人 UPDATE 並 COMMIT)。
T1:SELECT balance → 200
T2:UPDATE balance = 500; COMMIT
T1:SELECT balance → 500 ← 不一樣了!
Phantom Read 幻讀 #
同一 Transaction 裡,兩次相同的範圍查詢,結果筆數不同(因為中間有別人 INSERT 並 COMMIT)。
T1:SELECT COUNT(*) WHERE age > 18 → 5 筆
T2:INSERT 一筆 age=20; COMMIT
T1:SELECT COUNT(*) WHERE age > 18 → 6 筆 ← 多一筆!
四個隔離級別對照 #
| 隔離級別 | Dirty Read | Non-repeatable Read | Phantom Read | 效能 |
|---|---|---|---|---|
| Read Uncommitted | ✅ 可能 | ✅ 可能 | ✅ 可能 | 最快 |
| Read Committed | ❌ 防止 | ✅ 可能 | ✅ 可能 | 快 |
| Repeatable Read | ❌ 防止 | ❌ 防止 | ✅ 可能 | 中 |
| Serializable | ❌ 防止 | ❌ 防止 | ❌ 防止 | 最慢 |
MySQL(InnoDB)預設:Repeatable Read(且靠 MVCC 在這個級別也防住了大多數 Phantom Read)
PostgreSQL 預設:Read Committed
💡 面試官可能會追問 #
Q1:資料庫怎麼實作隔離? #
主要靠兩種機制:
- 鎖(Locking):讀鎖(S-lock)和寫鎖(X-lock),讀寫互斥
- MVCC(Multi-Version Concurrency Control):每個寫入保留舊版本,讀操作讀舊版本,讀寫不互相阻塞。PostgreSQL 和 MySQL 的 InnoDB 都用 MVCC
Q2:Repeatable Read 真的能防 Phantom Read 嗎? #
SQL 標準說 Repeatable Read 不防 Phantom Read,但 MySQL InnoDB 的實作靠 Next-Key Lock(Gap Lock + Record Lock)在 Repeatable Read 級別就防住了大多數 Phantom Read 場景。
Q3:什麼場景需要 Serializable? #
財務系統、庫存管理等資料正確性要求極高的場景。實務上很少直接用,通常靠 SELECT ... FOR UPDATE 或 LOCK IN SHARE MODE 在需要的地方精準加鎖,而不是把整個隔離級別拉到最高。