查data_locks前必须启用performance_schema对应consumer,否则返回空;LOCK_DATA仅在聚集或唯一索引加锁时非NULL,标识被锁主键值;data_lock_waits需联查data_locks和INNODB_TRX才能定位阻塞源头;IX为意向锁不阻塞DML,X,GAP为临键锁才是并发冲突主因。

查data_locks前必须确认performance_schema已启用对应consumer
直接查performance_schema.data_locks返回空,大概率不是没锁,而是锁采集被关了。MySQL 8.0默认不开启所有锁相关采集器,得手动打开:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_transactions_current', 'events_transactions_history', 'events_locks_current', 'events_locks_history');- 再查
SELECT * FROM performance_schema.setup_consumers WHERE NAME LIKE 'events%lock%';确认状态为YES - 重启连接或执行
FLUSH STATUS;后才生效,旧连接不会自动加载新配置
漏掉这步,后面所有data_locks查询都像在查一张空表——不是没数据,是根本没收集。
用data_locks定位具体哪行被锁住
data_locks里真正能帮你“看到锁在哪一行”的字段只有LOCK_DATA,但它有严格前提:只在聚集索引(主键)或唯一索引上加锁时才非NULL,且值就是被锁记录的主键值。
- 例如查到
LOCK_TYPE = 'RECORD'、LOCK_MODE = 'X'、LOCK_DATA = '123',基本可断定主键为123的那行正被排他锁持有 - 若
LOCK_DATA为空,可能是锁在二级索引上(此时需结合INDEX_NAME看),也可能是间隙锁(LOCK_MODE含GAP字样) -
OBJECT_SCHEMA和OBJECT_NAME要连起来看,格式是`db_name`.`table_name`,反引号不能忽略,否则解析出错
靠data_lock_waits理清谁在等谁
data_lock_waits不显示锁本身,只显示等待关系,关键字段是BLOCKING_TRX_ID和REQUESTING_TRX_ID。它和data_locks必须联查才能闭环:
- 先查
data_lock_waits拿到阻塞者事务ID:SELECT BLOCKING_TRX_ID FROM performance_schema.data_lock_waits LIMIT 1; - 再用这个ID去
data_locks里查它持有哪些锁:SELECT LOCK_TYPE, LOCK_MODE, LOCK_DATA, INDEX_NAME FROM performance_schema.data_locks WHERE ENGINE_TRANSACTION_ID = ?; - 同时查
INFORMATION_SCHEMA.INNODB_TRX确认该事务的TRX_QUERY和TRX_STARTED时间,判断是否是长事务未提交
单独看data_lock_waits只能知道“有人卡住了”,但不知道卡在什么资源上;单独看data_locks只知“锁在哪”,不知谁在等——必须交叉验证。
LOCK_MODE字段里的GAP和IX容易误读
LOCK_MODE值如X,GAP或IX不是并列关系,而是不同层级的锁类型,混在一起看会误导判断:
-
IX永远是LOCK_TYPE = 'TABLE',说明事务只申请了意向排他锁,还没真正加行锁(比如SELECT ... FOR UPDATE没命中任何行) -
X,GAP是LOCK_TYPE = 'RECORD',表示临键锁(record + gap),锁的是一个范围,不是单点 - 如果看到同一事务既有
LOCK_TYPE = 'TABLE'且LOCK_MODE = 'IX',又有LOCK_TYPE = 'RECORD'且LOCK_MODE = 'X',说明它既加了表级意向锁,又对具体行加了排他锁——这是正常行为,不是双重锁定
最常踩的坑是把IX当成“正在锁表”,其实它只是声明“我要锁里面某些行”,真正阻塞DDL的是它,但不影响其他DML;而X,GAP才是并发读写冲突的真正源头,尤其在范围查询或无索引更新时悄无声息地锁住大片。


















