萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL InnoDB 加鎖機制與死鎖分析全攻略

基於十年數據庫內核研發經驗,本文系統梳理 MySQL InnoDB 的加鎖規則、鎖模式、隔離級別差異、複雜 SQL 加鎖分析方法,並深度剖析兩類經典死鎖場景及一個「不可思議」的唯一索引刪除死鎖案例,助你徹底掌握鎖問題排查核心邏輯。

核心基礎概念

MVCC:快照讀 vs 當前讀

InnoDB 採用 MVCC(多版本並發控制),讀寫不衝突,極大提升 OLTP 並發性能。

  • 快照讀:普通 SELECT,讀取可見版本(可能是歷史版本),不加鎖。
  • 當前讀:SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE、INSERT/UPDATE/DELETE,讀取最新版本並加鎖(S 鎖或 X 鎖)。

Update 執行流程圖

圖示:UPDATE 內部包含當前讀,逐條加鎖、返回、修改,循環直至結束。

聚簇索引

InnoDB 採用聚簇索引表,完整行數據存儲在主鍵索引中;二級索引葉子節點存主鍵值,查詢需「回表」。

兩階段鎖協議

2PL 示意圖

加鎖階段只加不釋,解鎖階段只釋不加,兩階段不相交。

隔離級別對加鎖的影響

九大組合:一條簡單 DELETE 的加鎖全景圖

以 DELETE FROM t1 WHERE id = 10 為例,需明確五個前提:

  1. id 是否主鍵?
  2. 隔離級別?
  3. id 有無索引?
  4. 索引是否唯一?
  5. 執行計劃(索引掃描 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 加鎖分析:條件拆解三步走

複雜 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)。

複雜 SQL 加鎖圖

RR 隔離級別加鎖結論:

  1. Index Key 範圍加 GAP 鎖;
  2. Index Filter:支持 ICP 時不滿足條件不加記錄鎖,否則加;
  3. 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 鎖(公平調度機制)。

根因三部曲

  1. Delete 內部加鎖三策略(RR + 唯一索引等值查詢):
    • 記錄存在且有效 → X lock, no gap
    • 記錄存在但已標記刪除 → next-key lock X(鎖記錄 + 鎖 GAP,防止新記錄插入)
    • 記錄不存在 → GAP lock
  2. 頁面鎖與事務鎖死鎖預防策略:持有頁面鎖 (Latch) 時若事務鎖需等待,必須先釋放頁面鎖,再等待行鎖。等到行鎖後重新加頁面鎖、重新判斷記錄狀態、重新加鎖。
  3. 並發時序重現:

    死鎖時序圖

    1. 事務 0 持有頁面鎖,讀到記錄「有效」,準備加 X, no gap。
    2. 事務 1 持有頁面鎖,讀到記錄「有效」,準備加 X, no gap。
    3. 事務 2 持有頁面鎖,讀到記錄「已刪除」(事務 0 已提交),加 next-key X(含 GAP)。
    4. 事務 0/1 獲取行鎖時發現衝突,釋放頁面鎖等待。
    5. 事務 0 提交,事務 1 獲取 X, no gap。
    6. 事務 1 重新加頁面鎖,發現記錄已變「已刪除」,按策略升級為 next-key X,需等待事務 2 的 GAP 鎖。
    7. 事務 2 等待事務 1 的記錄鎖(公平調度)。死鎖形成!

觸發前提總結

  • Delete 唯一索引等值查詢(範圍刪除場景不同)
  • ≥ 3 個並發刪除同一存在的記錄
  • RR 隔離級別,未開啟 innodb_locks_unsafe_for_binlog
  • InnoDB 引擎

總結:掌握加鎖規則的知識圖譜

  1. 數據庫理論:存儲格式、MVCC vs Lock-CC、2PL、隔離級別定義
  2. SQL 執行計劃:主鍵/唯一/範圍/全掃描
  3. 實現細節:條件提取、ICP、Semi-Consistent Read
  4. 死鎖分析:加鎖順序不一致、逐條 SQL 分析加鎖序列

融會貫通,MySQL 加鎖規則盡在掌握。

copyright © 萬盛學電腦網 all rights reserved
隔離級別快照讀當前讀加鎖特點
Read Committed (RC)忽略記錄鎖;允許幻讀
Repeatable Read (RR,默認)忽略記錄鎖 + 間隙鎖;防止幻讀
Serializable退化為當前讀全部加鎖(讀加 S,寫加 X),並發度極低