mysql 8.0+中,performance_schema.data_locks是查看当前行锁类型的权威视图,lock_type='record'且lock_mode为'x,rec_not_gap'(记录锁)、'x,gap'(间隙锁)或'x,next_key'(临键锁)分别对应不同锁行为。

查 performance_schema.data_locks 看 LOCK_TYPE 和 LOCK_MODE
MySQL 8.0+ 中,data_locks 是唯一能直接告诉你“当前加了什么锁”的权威视图。它不靠推测,只展示内核实际持有的锁。
关键字段就两个:LOCK_TYPE 必须是 'RECORD'(否则不是行锁),LOCK_MODE 才决定具体类型:
-
LOCK_MODE = 'X,REC_NOT_GAP'→ 纯记录锁(Record Lock),比如SELECT ... FOR UPDATE精准命中主键 -
LOCK_MODE = 'X,GAP'→ 间隙锁(Gap Lock),常见于WHERE age > 25这类范围查询(RR 隔离级别) -
LOCK_MODE = 'X,NEXT_KEY'→ 临键锁(Next-Key Lock),即记录锁 + 间隙锁,RR 下普通索引等值查询的默认行为
注意:LOCK_MODE 值里带 GAP 就不含记录本身,带 NEXT_KEY 一定包含记录和左边间隙,REC_NOT_GAP 才是真正“只锁那一行”。
结合 INNODB_TRX 和 SQL 语句看隔离级别与执行计划
单看 data_locks 容易误判——同一句 SQL 在不同隔离级别下加的锁完全不同。
比如 SELECT * FROM t WHERE name = 'Alice' FOR UPDATE:
- 在
READ COMMITTED下 → 只加REC_NOT_GAP(无间隙锁) - 在
REPEATABLE READ下 → 若name是普通索引,大概率加NEXT_KEY(防止幻读)
所以必须关联 INNODB_TRX 查 TRX_ISOLATION_LEVEL,再用 EXPLAIN 确认是否走了索引、是否全表扫描。如果没走索引,InnoDB 可能对每条记录都加 REC_NOT_GAP,看起来像“行锁”,实则是“伪表锁”效果。
用 SHOW ENGINE INNODB STATUS\G 验证锁等待链
当事务卡住时,data_locks 只告诉你“持有什么锁”,而 SHOW ENGINE INNODB STATUS 的 TRANSACTIONS 部分会暴露“为什么卡住”。
重点找这三类线索:
-
WAITING FOR THIS LOCK TO BE GRANTED:后面跟lock_mode X locks rec but not gap→ 明确是记录锁等待 - 出现
lock_mode X locks gap before rec→ 正在等间隙锁释放 - 若只写
Trx has been waiting 47 sec却没列锁模式 → 很可能不是 InnoDB 行锁问题,而是 MDL 或其他 Server 层锁
这个命令输出是实时快照,比视图更贴近真实阻塞现场,但要注意它只保留最近一次死锁或等待信息,需及时抓取。
别忽略隐式锁和意向锁的干扰
意向锁(IS/IX)本身不阻塞读写,但它是行锁的前提。你看到 data_locks 里有 LOCK_TYPE = 'TABLE' 且 LOCK_MODE 含 IX,说明事务已声明“我要对这张表加行锁”,但还没落到具体记录上——这时候查不到 RECORD 锁是正常的。
隐式锁更隐蔽:插入新记录前,InnoDB 不立即加锁,而是等另一个事务来查这条“未提交”的记录时,才将隐式锁转为显式锁。这种情况下,data_locks 初期为空,但 SHOW ENGINE INNODB STATUS 的等待描述里会出现 holds the lock 却找不到对应锁记录。
真正难定位的,往往是这种“锁还没显形,但阻塞已经发生”的情况。











