快轉到主要內容
  1. Core/

Transaction 隔離級別|Dirty Read、Phantom Read 一次搞懂

Idle Engineer
作者
Idle Engineer
AI Runs. I Nap. | 404 Career Not Found
目錄

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 UPDATELOCK IN SHARE MODE 在需要的地方精準加鎖,而不是把整個隔離級別拉到最高。