📌 关键词:间隙锁、锁等待、performance_schema、SHOW ENGINE INNODB STATUS、死锁
大家好呀!我是数据库小学妹👋
上一篇我们搞懂了MVCC和锁联手如何解决幻读,知道了Next-Key Lock(行锁+间隙锁)是RR隔离级别下的“幻读克星”。
但理论懂了,实战中遇到问题时,怎么快速定位?比如,事务突然卡住、插入被阻塞——这背后很可能藏着一个间隙锁幽灵在作祟!
今天我们就用MySQL自带的监控工具,手把手揪出间隙锁,让问题无所遁形!
一、场景模拟:间隙锁引发的“插入阻塞”
假设订单表 orders 有索引字段 order_date。事务A执行了范围锁定:
--- 事务A
BEGIN;
SELECT * FROM orders
WHERE order_date BETWEEN '2026-05-01' AND '2026-05-10'
FOR UPDATE;
-- 事务A 未提交
此时,事务B试图插入一条新订单:
-- 事务B(另一个会话)
INSERT INTO orders (order_id, order_date) VALUES (1005, '2026-05-05');
🌈结果:事务B被卡住不动。
🚩原因:事务A的间隙锁锁住了 order_date 在 [2026-05-01, 2026-05-10] 范围内的“间隙”,导致新插入被阻塞。
二、排查利器1:SHOW ENGINE INNODB STATUS——快速定位锁等待
当系统出现卡顿时,第一步:登录MySQL,执行:
SHOW ENGINE INNODB STATUS\G
在输出中搜索 TRANSACTIONS 段落,找到处于 LOCK WAIT 的事务。关键信息:
WAITING FOR THIS LOCK TO BE GRANTED:显示正在等待的锁类型(如lock_mode X locks gap before rec)- 锁的范围:例如
RECORD LOCKS ... index order_date ... lock_mode X locks gap before rec - 阻塞事务ID:找到持有锁的事务(事务A)
💡 如果看到
LATEST DETECTED DEADLOCK,说明发生了死锁(InnoDB已自动回滚一个事务),需进一步分析。
三、排查利器2:performance_schema——深入锁细节
MySQL 5.7+版本提供了更强大的性能监控工具performance_schema。
执行以下查询,直接获取锁等待关系:
SELECT
OBJECT_SCHEMA, OBJECT_NAME,
INDEX_NAME,
LOCK_TYPE, LOCK_MODE,
LOCK_STATUS,
BLOCKING_ENGINE_TRANSACTION_ID,
REQUESTING_ENGINE_TRANSACTION_ID
FROM performance_schema.data_lock_waits
JOIN performance_schema.data_locks
ON data_lock_waits.REQUESTING_ENGINE_LOCK_ID = data_locks.ENGINE_LOCK_ID;
🔖重点关注字段:
LOCK_MODE = 'X,GAP'→ 排他间隙锁LOCK_STATUS = 'WAITING'→ 谁在等BLOCKING_ENGINE_TRANSACTION_ID→ 谁在阻塞
拿到阻塞事务ID后,到 INNODB_TRX 表查具体SQL:
SELECT trx_id, trx_state, trx_started, trx_query
FROM information_schema.INNODB_TRX
WHERE trx_id = 阻塞事务ID;
四、实战解读:一个典型的间隙锁日志
执行 SHOW ENGINE INNODB STATUS 后,你会看到类似这样的片段:
---TRANSACTION 31045, ACTIVE 10 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 8, query id 123 localhost root
SELECT * FROM orders WHERE status = 'pending' FOR UPDATE
---TRANSACTION 31046, ACTIVE 5 sec
1 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 9, query id 124 localhost root
INSERT INTO orders (id, status, amount) VALUES (7, 'pending', 150)
------- TRX HAS BEEN WAITING 5 sec FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 28 page no 4 n bits 72 index idx_status of table `test`.`orders` trx id 31046 lock_mode X locks gap before rec
🌈结论:事务31046(INSERT)在等待一个 lock_mode X locks gap before rec(间隙锁),持有者是事务31045(FOR UPDATE查询)。
五、避坑指南:间隙锁排查的3个易错点
| 易错点 | 正确理解 |
|---|---|
| 间隙锁 ≠ 死锁 | 间隙锁导致锁等待(需手动处理);死锁自动回滚,日志会记录 |
| RC级别无间隙锁 | 读已提交(RC)下不会产生间隙锁。若performance_schema仍显示gap,可能是视图残留,以实际隔离级别为准 |
| 无索引 = 表锁陷阱 | WHERE条件无索引时,间隙锁会升级为全表锁,性能暴跌!务必先用EXPLAIN检查 |
👋 我是数据库小学妹一个用设计师思维学数据库的转行人。我们一起把复杂的技术变得简单有趣!💕
本文示例基于 MySQL 8.0。不同版本锁表名有差异(5.7用INNODB_LOCKS等),请自行适配。