Home
Posts
Categories
Series
Tags
About
Mysql 的 Repeatable Read & 幻讀
postedOn: 2026-4-12   updatedOn: 2026-4-12   includedIn: 程式
wordsCount: 621   readingTime: 2 mins   viewers:

此篇討論Mysql RR下,預設解決了哪些幻讀,哪些沒解決?如何處理剩下的幻讀問題

定義

快照讀 :

  • select

當前讀 :

  • select xxx lock in share mode
  • select xxx for update
  • insert (先select 當前)
  • update (先select 當前)
  • delete (先select 當前)

Mysql RR 的實現= MVCC + Next-Key Lock

幻讀可能性

1.交易都是快照讀=>不會幻讀

Transaction A Transaction B
初始狀態 (V0): user { Ian, 21 }
Start Start
Select user Where age > 20 (A);/* 結果只有 Ian */
Insert user { Mark, 30 } (V1);Commit;
Select user Where age > 20 (B);/* 結果只有 Ian */
Commit;
核心邏輯:因為 Select 只被允許讀取事務 B 開始時的版號,也就是只會讀取到『V0』的資料狀態。

2. 交易一開始就當前讀,所以next-key lock=>不會幻讀

Transaction A Transaction B
初始狀態: user { Mark, 2 }
Start Start
Select user Where age < 5 For update (A);觸發鎖範圍 (-∞, 5)
Insert user { CC, 8 }Ok (8 不在鎖範圍內)
Insert user { AA, 4 }卡住! (4 在鎖範圍內)
Insert user { BB, 3 }卡住! (3 在鎖範圍內)
Commit
Select user Where age < 5 For update (B)
Commit
最終結果:(A) 查詢結果只有 Mark(B) 查詢結果只有 Mark

3. 幻讀(先用快照讀,後用隱性當前讀破壞版本,又用快照讀發現資料改變

Transaction A Transaction B
初始狀態 (V0): user { Ian, 21 }
Start Start
Select user Where age > 20 (A);/* 讀取 V0:只有 Ian */
Insert user { Mark, 30 } (V1);Commit;
Select user Where age > 20 (B);/* 讀取 V0:只有 Ian */
Update user set name = 'MarK' Where age = 30;/* 關鍵: 成功更新了本來「看不見」的 V1 資料 */
Select user Where age > 20 (C);/* 讀取 V1:出現了 Ian 與 Mark */
Commit;

查詢結果摘要:

  • (A) select:結果只有 Ian
  • (B) select:結果只有 Ian
  • (C) select:結果有 Ian, Mark

總結

開啟交易後

1
2
3
4
if 快照讀select的後面,有使用當前讀語句
    一開始select就必須用 select...For update
else
    才可用快照讀select