首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL InnoDB 間隙鎖導致死鎖深度剖析與避坑指南

     編輯:MySQL 綜合教學

現象複現

表 preferences 僅有主鍵 numericId,無資料。兩個事務並發執行 UPDATE ... WHERE numericId = 不存在的值,隨後各自 INSERT 該值,觸發死鎖。

核心機制:Next-Key Lock = Record Lock + Gap Lock

  • Record Lock:鎖定索引記錄本身。
  • Gap Lock:鎖定索引記錄之間的「間隙」,防止幻讀。
  • Next-Key Lock:左開右閉區間 (prev, curr],InnoDB REPEATABLE READ 預設鎖模式。

死鎖演變過程

  1. Session A: BEGIN; UPDATE preferences SET ... WHERE numericId = 1; → 未命中記錄,獲得 (-∞, 1] Next-Key Lock(Gap Lock 部分)。
  2. Session B: BEGIN; UPDATE preferences SET ... WHERE numericId = 2; → 獲得 (1, 2] Next-Key Lock。
  3. Session A: INSERT INTO preferences VALUES (1, ...); → 需在 (-∞, 1] 間隙插入,請求插入意向鎖,被 Session B 持有的 (1, 2] Gap Lock 封鎖(因 1 在該區間邊界),進入 LOCK WAIT。
  4. Session B: INSERT INTO preferences VALUES (2, ...); → 同理被 Session A 的 Gap Lock 封鎖 → 死鎖,InnoDB 檢測回滾 Session B。

避免策略

策略原理適用場景
改用 REPLACE INTO / INSERT ... ON DUPLICATE KEY UPDATE單語句原子化,避免「先查後寫」間隙業務允許覆蓋寫入
降級隔離級別 READ COMMITTEDRC 下無 Gap Lock,僅 Record Lock可接受不可重複讀、無幻讀需求
innodb_locks_unsafe_for_binlog=ON (已棄用)關閉 Gap Lock,僅用於測試不建議生產
應用層樂觀鎖(version 欄位)避免長事務持有鎖高併發更新熱點行
預先 INSERT 佔位行消除間隙,變 Record Lock 競爭業務可預先初始化

驗證隔離級別影響

-- Session 1
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
UPDATE preferences SET ... WHERE numericId = 1;  -- 僅 Record Lock,無 Gap Lock
-- Session 2 同理,INSERT 不會被 Gap Lock 封鎖,僅主鍵衝突等待

監控與診斷

SHOW ENGINE INNODB STATUS\G
-- 關注 LATEST DETECTED DEADLOCK 區塊,分析鎖等待鏈
SELECT * FROM performance_schema.data_locks WHERE ENGINE='INNODB';
SELECT * FROM sys.innodb_lock_waits;

總結

InnoDB 間隙鎖是 REPEATABLE READ 防幻讀的代價。「不存在記錄的 UPDATE/DELETE 會持有 Gap Lock」 是死鎖高發根因。架構層面:優先用單語句 UPSERT 替代「查詢-決策-寫入」模式;必要時降級 RC 隔離級別。應用層:縮短事務、避免長事務、監控 Innodb_deadlocks 計數器告警。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved