第五章:併發控制 (MVCC & Locks) — 平行宇宙的魔法
Published: Thu Feb 05 2026 | Modified: Fri Feb 06 2026 , 2 minutes reading.
1. 定義
MVCC (Multi-Version Concurrency Control) 是一種併發控制方法,常用於資料庫中以提供對數據的併發存取。
核心思想是:讀寫分離,互不阻塞。 當一個交易正在寫某行數據時,讀交易不會被阻塞,而是會讀取該行數據的一個舊版本(快照)。這就像每個人都在看數據的不同「平行宇宙」。
2. 技術深度:Undo Log 與 Read View
以 MySQL InnoDB 為例:
- Undo Log:當交易修改數據時,InnoDB 不會覆蓋舊數據,而是將舊數據複製到 Undo Log 中,並透過指標形成版本鏈。
- Read View:當交易啟動時,InnoDB 會生成一個 Read View,記錄當前所有「活躍」(未提交)的交易 ID。
- 可見性判斷:在讀取數據時,交易會拿數據行的版本號與 Read View 對比。如果版本號屬於「活躍」交易,說明該版本不可見,於是順著 Undo Log 找上一個版本。
3. 可視化:快照讀 (Snapshot Read)
sequenceDiagram
participant TxA as 交易 A (讀取者)
participant TxB as 交易 B (寫入者)
participant DB as 數據行 (ID=1)
participant Undo as Undo Log
TxB->>DB: 1. UPDATE ID=1 SET Age=30 (原 Age=20)
Note over DB: 行被鎖定 (X-Lock)<br/>Age 變為 30<br/>生成 Undo Log: Age=20
TxA->>DB: 2. SELECT * FROM users WHERE ID=1
Note over DB: 偵測到行被 TxB 鎖定
DB->>Undo: 3. TxA 讀取 Undo Log 中的舊版本
Undo-->>TxA: 4. 回傳 Age=20 (快照)
TxB->>DB: 5. COMMIT
Note over DB: 數據 Age=30 正式生效4. 真實案例:GitHub 的主鍵衝突故障 (2018)
背景:GitHub 在進行 MySQL 資料庫遷移時。 現象:網站短暫不可用,寫入失敗。
原因:Auto-increment 鎖競爭與 Next-Key Lock。 雖然 MVCC 解決了讀寫衝突,但寫寫衝突依然需要鎖定。
- Auto-inc Lock: 在進行大規模數據插入(INSERT INTO … SELECT)時,MySQL 的自增鎖在高併發下成為了瓶頸。
- Next-Key Lock: 在 RR(可重複讀取)隔離級別下,為了防止幻讀,插入前的唯一性檢查(如
REPLACE INTO或INSERT ON DUPLICATE)可能會觸發 Next-Key Lock(鎖定索引間隙),具體取決於索引類型與執行計畫。在極高併發的衝突場景下極易引發嚴重的鎖定等待。
教訓:MVCC 並不是萬能的。在極高併發的寫入場景下,必須深入理解鎖定的粒度(Record vs. Gap vs. Next-Key)。
5. 深度最佳化與縱深防禦
A. 隔離級別的選擇
- Read Committed (RC):每次查詢都生成新的 Read View。適合對即時性要求高的業務。
- Repeatable Read (RR):交易啟動時生成一次 Read View。MySQL 預設級別;透過 Next-Key 鎖緩解了「鎖定讀取」場景下的幻讀問題,但普通快照讀取在不同實現下仍可能觀察到範圍異常。
B. 避免長交易
- 長交易意味著 Read View 需要保留很久。
- 這會導致 Undo Log 無法被清理(Purge),導致系統空間膨脹(History List Length 飆升),進而拖慢所有查詢(因為每次都要走過很長的版本鏈)。
C. 樂觀鎖 (Optimistic Locking)
對於非強一致性場景,可以在應用層使用版本號實現樂觀鎖,從而完全避免資料庫層面的行鎖。
UPDATE products SET stock = stock - 1
WHERE id = 1 AND version = 5;6. 參考資料
- MySQL 8.0 Reference: InnoDB Multi-Versioning
- VLDB Journal: An Empirical Evaluation of In-Memory MVCC
- GitHub Engineering: MySQL High Availability
