基於十年數據庫內核研發經驗,本文系統梳理 MySQL InnoDB 的加鎖規則、鎖模式、隔離級別差異、複雜 SQL 加鎖分析方法,並深度剖析兩類經典死鎖場景及一個「不可思議」的唯一索引刪除死鎖案例,助你徹底掌握鎖問題排查核心邏輯。
| 隔離級別 | 快照讀 | 當前讀加鎖特點 |
| Read Committed (RC) | 忽略 | 記錄鎖;允許幻讀 |
| Repeatable Read (RR,默認) | 忽略 | 記錄鎖 + 間隙鎖;防止幻讀 |
| Serializable | 退化為當前讀 | 全部加鎖(讀加 S,寫加 X),並發度極低 |
九大組合:一條簡單 DELETE 的加鎖全景圖
以 DELETE FROM t1 WHERE id = 10 為例,需明確五個前提:
- id 是否主鍵?
- 隔離級別?
- id 有無索引?
- 索引是否唯一?
- 執行計劃(索引掃描 vs 全表掃描)?
組合一:id 主鍵 + RC

僅在主鍵索引 id=10 記錄上加 X 鎖。
組合二:id 唯一索引 + RC

兩把 X 鎖:唯一索引記錄 + 聚簇索引對應記錄(防止主鍵更新衝突)。
組合三:id 非唯一索引 + RC

所有滿足條件的二級索引記錄 + 對應聚簇索引記錄均加 X 鎖。
組合四:id 無索引 + RC

全表掃描,聚簇索引所有記錄先加 X 鎖,Server 層過濾後釋放不滿足條件的鎖(違背 2PL,但為性能妥協)。
組合五/六:id 主鍵/唯一索引 + RR
加鎖行為與 RC 相同,因唯一性天然防止幻讀,無需 GAP 鎖。
組合七:id 非唯一索引 + RR —— GAP 鎖登場

在滿足條件的記錄區間前後加 GAP 鎖,阻止新記錄插入,從而防止幻讀。
掃描到第一條不滿足條件記錄時,不加記錄鎖,但仍加 GAP 鎖。
組合八:id 無索引 + RR —— 全表鎖死

聚簇索引所有記錄加 X 鎖 + 所有 GAP 加 GAP 鎖。表僅剩快照讀可用。可通過 innodb_locks_unsafe_for_binlog 觸發 semi-consistent read 緩解,但不建議生產開啟。
組合九:Serializable
所有讀變當前讀,MVCC 退化為鎖並發控制,不建議使用。
複雜 SQL 加鎖分析:條件拆解三步走

WHERE 條件拆解為三類:
- Index Key:決定索引掃描範圍(如
pubtime > 1 AND pubtime < 20)。
- Index Filter:可在索引上過濾但非 Key(如
userid = 'hdc'),是否下推取決於 ICP (Index Condition Pushdown)。
- Table Filter:只能在聚簇索引/Server 層過濾(如
comment IS NOT NULL)。

RR 隔離級別加鎖結論:
- Index Key 範圍加 GAP 鎖;
- Index Filter:支持 ICP 時不滿足條件不加記錄鎖,否則加;
- Table Filter:無論是否滿足,聚簇索引記錄均加 X 鎖。
死鎖原理與分析方法
核心結論:死鎖與 SQL 條數無關,關鍵在於多 Session 加鎖順序不一致。
案例一:兩條 SQL 交叉死鎖

Session 1 先鎖表 A 再鎖表 B,Session 2 相反,經典循環等待。
案例二:單條 SQL 死鎖(不同索引掃描順序相反)

Session 1 走 name 索引 → 聚簇索引順序 [1,hdc,100] → [6,hdc,10]
Session 2 走 pubtime 索引 → 聚簇索引順序 [6,hdc,10] → [1,hdc,100]
加鎖順序相反,極易死鎖。
深度案例:唯一索引單行刪除的「不可思議」死鎖
場景:表 dltask,主鍵 id,唯一索引 uniq_a_b_c(a,b,c),RR 隔離級別。
三個並發事務同時執行 DELETE FROM dltask WHERE a=? AND b=? AND c=?,刪除同一條記錄,產生死鎖。


死鎖日誌讀解關鍵點
- 事務 1:等待
next-key lock X(鎖記錄 + 鎖 GAP)。
- 事務 2:持有
X lock with no gap(僅鎖記錄),等待 next-key lock X。
- 循環等待:事務 1 等事務 2 的記錄鎖,事務 2 等事務 1 的 GAP 鎖(公平調度機制)。
根因三部曲
- Delete 內部加鎖三策略(RR + 唯一索引等值查詢):
- 記錄存在且有效 →
X lock, no gap
- 記錄存在但已標記刪除 →
next-key lock X(鎖記錄 + 鎖 GAP,防止新記錄插入)
- 記錄不存在 →
GAP lock
- 頁面鎖與事務鎖死鎖預防策略:持有頁面鎖 (Latch) 時若事務鎖需等待,必須先釋放頁面鎖,再等待行鎖。等到行鎖後重新加頁面鎖、重新判斷記錄狀態、重新加鎖。
- 並發時序重現:

- 事務 0 持有頁面鎖,讀到記錄「有效」,準備加
X, no gap。
- 事務 1 持有頁面鎖,讀到記錄「有效」,準備加
X, no gap。
- 事務 2 持有頁面鎖,讀到記錄「已刪除」(事務 0 已提交),加
next-key X(含 GAP)。
- 事務 0/1 獲取行鎖時發現衝突,釋放頁面鎖等待。
- 事務 0 提交,事務 1 獲取
X, no gap。
- 事務 1 重新加頁面鎖,發現記錄已變「已刪除」,按策略升級為
next-key X,需等待事務 2 的 GAP 鎖。
- 事務 2 等待事務 1 的記錄鎖(公平調度)。死鎖形成!
觸發前提總結
- Delete 唯一索引等值查詢(範圍刪除場景不同)
- ≥ 3 個並發刪除同一存在的記錄
- RR 隔離級別,未開啟
innodb_locks_unsafe_for_binlog
- InnoDB 引擎
總結:掌握加鎖規則的知識圖譜
- 數據庫理論:存儲格式、MVCC vs Lock-CC、2PL、隔離級別定義
- SQL 執行計劃:主鍵/唯一/範圍/全掃描
- 實現細節:條件提取、ICP、Semi-Consistent Read
- 死鎖分析:加鎖順序不一致、逐條 SQL 分析加鎖序列
融會貫通,MySQL 加鎖規則盡在掌握。
copyright ©
萬盛學電腦網 all rights reserved