Next-Key Lock = Record Lock(記錄鎖) + Gap Lock(間隙鎖),鎖定一個**左開右閉**區間 (前一記錄值, 當前記錄值]。
| 場景 | 加鎖類型 | 條件 |
|---|---|---|
| 唯一索引等值查詢 (PK/UK) | Record Lock | 命中記錄 → 降級為 Record Lock;未命中 → Gap Lock |
| 非唯一索引等值查詢 | Next-Key Lock | 掃描到的每條記錄加 Next-Key Lock,間隙加 Gap Lock |
| 範圍查詢 (>, <, >=, <=, BETWEEN) | Next-Key Lock | 掃描範圍內所有記錄加 Next-Key Lock,邊界間隙加 Gap Lock |
| 全表掃描 | Next-Key Lock | 聚簇索引所有記錄加 Next-Key Lock,實質鎖全表 |
| INSERT | 插入意向鎖 | 屬 Gap Lock 一種,多事務可同時持有,互不阻塞;遇 Gap/Next-Key Lock 阻塞 |
Session 1: BEGIN; DELETE FROM t WHERE a=11; -- 加鎖 (11,11] 即 Record Lock Session 2: BEGIN; INSERT 10, 15, 9, 16; -- 全部成功,無阻塞
✅ 預期內:唯一索引等值命中,降級 Record Lock,僅鎖該行。
Session 1: BEGIN; DELETE FROM t WHERE a=22; -- 掃描到 supremum pseudo-record,加鎖 (14, +∞) Session 2: INSERT 201, 20, 19, 18, 16; -- 全部阻塞 (Lock wait timeout) Session 2: INSERT 9; -- 成功,不在鎖區間
🔑 關鍵:InnoDB 為防止幻讀,將「不存在的值」視為落在「最後一條記錄後的間隙」,鎖定 (max_value, +∞)。
數據: 7,9,10,12,13,14,15,22,23,24,25 Session 1: BEGIN; DELETE FROM t WHERE a=21; -- 掃描到 22,加鎖 (15,22] Session 2: INSERT 20, 21, 16; -- 阻塞 Session 2: INSERT 26, 6; -- 成功
🔑 鎖區間精確縮小到 (前一記錄, 目標值]。
官方定義:「Next-Key Lock = Record Lock on index record + Gap Lock on gap before the index record」。掃描方向由優化器決定,為保證範圍查詢完整性,InnoDB 採用「掃描到的記錄 + 其前方間隙」策略,自然形成左開右閉。
-- Session 1 DELETE FROM t WHERE id IN (100, 200, 300); -- 按索引順序加鎖 100→200→300 -- Session 2 DELETE FROM t WHERE id IN (300, 200, 100); -- 按 300→200→100 加鎖 -- 死鎖:1 持有 100 等 200,2 持有 300 等 200
修正:應用層**統一按主鍵升序**處理批量操作。
-- 查看當前事務鎖等待 SELECT * FROM performance_schema.data_locks; SELECT * FROM sys.innodb_lock_waits; -- 死鎖日志 SHOW ENGINE INNODB STATUS\G -- LATEST DETECTED DEADLOCK 區塊 -- 開啟鎖監控 (5.7+) SET GLOBAL innodb_print_all_deadlocks=ON; SET GLOBAL innodb_status_output=ON; SET GLOBAL innodb_status_output_locks=ON;
performance_schema 與 sys schema 可視化鎖等待,從「感覺慢」進化到「精準定位」。