因为data_locks只记录锁本身(持有或等待中的锁),不记录等待关系,无法体现“谁在等谁”;必须结合data_lock_waits,其blocking_engine_transaction_id和requesting_engine_transaction_id字段才直接映射阻塞者与等待者。

为什么直接查performance_schema.data_locks看不出谁在等谁?
data_locks只记录锁本身(持有或等待中的锁),不记录等待关系。它告诉你“某事务在锁某行”,但不告诉你“这个锁正被另一个事务等着”。所以单看这张表,你只能看到一堆LOCK_STATUS = 'GRANTED'或'WAITING'的锁,却无法串联起阻塞链。
真正要定位“谁卡了谁”,必须结合performance_schema.data_lock_waits——它专为描述等待关系设计,字段BLOCKING_ENGINE_TRANSACTION_ID和REQUESTING_ENGINE_TRANSACTION_ID就是阻塞者与等待者的直接映射。
-
data_locks.LOCK_STATUS = 'WAITING'的记录,不一定出现在data_lock_waits里:有些等待可能刚发生、还没被采集到,或因性能模式采样延迟漏掉 -
data_locks中LOCK_DATA字段值(如"10086")不能直接当SQL条件用:它只是InnoDB内部标识,需结合INDEX_NAME和表结构反推查询条件 - 同一事务可能在
data_locks中有多条记录(多个索引、多个页、多个行),但data_lock_waits里只体现一次等待关系
怎么用data_lock_waits快速揪出“最堵的阻塞源头”?
先查谁被等得最多,而不是从头遍历所有锁。目标是识别那个BLOCKING_ENGINE_TRANSACTION_ID下挂了最多等待者的事务——它大概率就是热点瓶颈。
执行这条聚合查询:
SELECT BLOCKING_ENGINE_TRANSACTION_ID, COUNT(*) AS wait_count FROM performance_schema.data_lock_waits GROUP BY BLOCKING_ENGINE_TRANSACTION_ID ORDER BY wait_count DESC LIMIT 5;
- 结果里的
BLOCKING_ENGINE_TRANSACTION_ID就是你要KILL的候选ID - 如果
wait_count超过业务容忍阈值(比如 > 3),且对应事务TRX_STARTED时间很早,基本可以判定为长事务持锁 - 注意:该ID不是
INNODB_TRX.trx_id,而是INNODB_TRX.trx_mysql_thread_id,需用它去关联threads表找真实连接
如何把data_lock_waits和data_locks连起来看具体锁在哪一行?
拿到阻塞事务ID后,下一步是确认它锁住了哪些具体数据。关键路径是:data_lock_waits → data_locks(通过BLOCKING_ENGINE_TRANSACTION_ID)→ 表结构反查。
示例查询(假设阻塞事务ID为12345):
SELECT d.OBJECT_SCHEMA, d.OBJECT_NAME, d.INDEX_NAME, d.LOCK_TYPE, d.LOCK_MODE, d.LOCK_DATA FROM performance_schema.data_locks d WHERE d.ENGINE_TRANSACTION_ID = 12345 AND d.LOCK_STATUS = 'GRANTED';
-
LOCK_TYPE = 'RECORD'且INDEX_NAME = 'PRIMARY'时,LOCK_DATA通常是主键值,可直接用于WHERE id = ... -
INDEX_NAME是非唯一二级索引时,LOCK_DATA可能是"value, pk_value"格式,需拆解后联合查询 - 若
LOCK_DATA为空或显示NULL,说明锁的是间隙(Gap Lock)或Next-Key Lock,此时需结合INDEX_NAME和相邻记录范围判断
为什么查data_locks会拖慢整个实例?
这不是错觉。频繁全表扫描performance_schema.data_locks(尤其在大事务持有数万行锁时)会争抢trx_sys->mutex,导致所有新事务创建被串行化,表现为begin变慢、连接堆积。
- 监控脚本每5秒执行一次
SELECT COUNT(*) FROM performance_schema.data_locks,就足以让高并发写入场景响应延迟飙升 -
data_locks底层依赖InnoDB的锁系统快照,锁越多、扫描越久;MySQL 8.0.24+虽优化了部分路径,但仍不建议高频轮询 - 临时规避方法:改用
SHOW ENGINE INNODB STATUS\G中的TRANSACTIONS段落,它只输出活跃事务摘要,开销小得多
真正需要锁定具体行时,别扫全表,先用data_lock_waits缩小事务范围,再按ENGINE_TRANSACTION_ID精准查data_locks——这是唯一能兼顾效率和精度的做法。











