隔離級別是一組「我願意容忍哪些並發異常」的契約。級別越高、異常越少,但並發度與吞吐越低。理解它要從會發生什麼壞事反推,而不是背名字。
🧠 Intuition
TIP
不要記「四個級別」,要記「四種異常」:髒讀、不可重複讀、幻讀、寫偏斜。每個級別只是「擋掉前 N 種」的開關。問題永遠是:我的業務不能忍受哪一種?
- 髒讀 (Dirty Read):讀到別人還沒 commit 的資料。
- 不可重複讀 (Non-repeatable Read):同一筆資料讀兩次,值變了。
- 幻讀 (Phantom):同一查詢範圍讀兩次,列數變了(別人插了新列)。
- 寫偏斜 (Write Skew):兩個交易各自讀同一份前提、各自寫不同列,合起來破壞了不變量。
⚖️ Tradeoffs
Read Committed — 擋髒讀
- 只讀已 commit 的值,寫加行鎖直到 commit。
- 仍會發生不可重複讀與幻讀。
- 多數資料庫的預設(如 PostgreSQL)。
Snapshot Isolation / Repeatable Read — 擋不可重複讀
- 交易看到「開始那一刻」的一致快照,靠 MVCC 實作(讀不擋寫)。
- 擋掉髒讀與不可重複讀;PostgreSQL 的 SI 連幻讀也擋掉,但仍會寫偏斜。
- 讀多寫少的 OLTP 甜蜜點:高並發又有一致快照。
Serializable — 擋全部,含寫偏斜
- 效果等同交易一個個排隊執行。實作有三條路:
- 實際序列化執行(單執行緒,如 VoltDB / Redis)。
- 兩階段鎖 (2PL):讀寫都加鎖,正確但易卡、吞吐低。
- SSI(可序列化快照隔離):MVCC + 偵測危險讀寫相依,衝突才 abort 重試——樂觀,高並發下勝出。
🔑 Takeaways
- 先問業務「不能忍哪種異常」,再挑能擋掉它的最低級別——級別越高代價越大。
- Snapshot Isolation = MVCC,是讀多寫少 OLTP 的實務甜蜜點,但記得它擋不住寫偏斜。
- 需要跨列不變量(排班、餘額、唯一性)就上 Serializable,現代實作首選 SSI(樂觀、衝突才重試)。
- 「Repeatable Read」在不同資料庫語意不同,看文件別看名字。
2026-07-01
- Q: Snapshot Isolation 既然連幻讀都能擋,為什麼還會寫偏斜?
- A: 因為 SI 保證「讀到一致快照」,但兩個交易讀的是同一份舊快照、各自更新不同的列,彼此的寫互相看不到,於是合起來違反了「至少留一個值班醫師」這類跨列不變量。SI 只偵測「寫到同一列」的衝突,偵測不到「基於同一前提寫不同列」。要擋它得上 Serializable(SSI 會偵測這種讀寫相依)。