mysql 8.0+需通过performance_schema.data_lock_waits查锁等待,但须先启用相关消费者;其blocking_engine_lock_id和requesting_engine_lock_id明确标识持锁与等锁线程,结合data_locks、threads和events_statements_current可定位sql及行级锁详情。

查哪个线程在等锁:看 performance_schema.data_lock_waits
MySQL 8.0+ 的锁等待关系不藏在 INFORMATION_SCHEMA.INNODB_TRX 里了,得靠 performance_schema 的三张表联动。最直接的入口是 data_lock_waits,它明确记录了“谁在等谁的锁”。但注意:这张表默认是关闭的,必须提前开启。
-
performance_schema必须启用(performance_schema=ON),且data_locks和data_lock_waits的消费者也要打开:UPDATE performance_schema.setup_consumers SET ENABLED='YES' WHERE NAME IN ('events_statements_current', 'data_locks', 'data_lock_waits'); - 查等待链:用
SELECT * FROM performance_schema.data_lock_waits;,结果里BLOCKING_ENGINE_LOCK_ID对应持有锁的线程,REQUESTING_ENGINE_LOCK_ID是卡住的那个 - 如果返回空,不代表没锁争用——可能是锁已释放、或消费者没开全、或你查的会话还没走到加锁逻辑(比如事务刚 START,还没 UPDATE)
定位持锁线程的 SQL 和状态:连查 data_locks + threads + events_statements_current
光知道线程 ID 不够,得看到底执行了什么语句、事务处于什么状态。data_locks 能告诉你锁在哪张表、哪个索引、哪一行(通过 OBJECT_SCHEMA、INDEX_NAME、LOCK_DATA),但它的 THREAD_ID 是内部 ID,得关联 threads 表转成 PROCESSLIST_ID,再和 events_statements_current 拼出当前正在跑的语句。
-
LOCK_DATA字段值如果是supremum pseudo-record,说明是间隙锁(Gap Lock),不是真数据行;如果是数字或字符串,大概率是聚簇索引主键值 - 注意
events_statements_current只保存当前正在执行的语句,如果事务已提交/回滚或语句执行完了,这里就是空的——得结合events_transactions_current看事务起始时间 - 别直接用
PROCESSLIST_ID去杀进程,先确认是不是业务关键会话;KILL CONNECTION xxx会强制中断,可能引发应用重试风暴
为什么 SHOW ENGINE INNODB STATUS 有时看不到锁信息
这个命令输出的 TRANSACTIONS 部分确实会列锁,但它只展示“最近一次”被检测到的死锁或显式锁等待,不是实时快照。而且它不显示锁的具体行记录(LOCK_DATA),只说“lock_mode X locks rec but not gap waiting”,对排查具体哪一行卡住帮助有限。
- 输出内容受
innodb_status_output_locks控制(MySQL 5.6.16+),默认 OFF;设为 ON 才会在SHOW ENGINE INNODB STATUS中包含LOCK WAIT详细段落 - 即使开了,它也不反映所有活跃锁,只记录 InnoDB 最近监控到的冲突点,适合快速抓典型问题,不适合做全量锁审计
- 它的线程 ID 是
Trx id(十六进制),和performance_schema.THREAD_ID无法直接对应,交叉验证时容易绕晕
避免误判:区分“锁等待”和“锁升级”导致的假性阻塞
有时候你看到一个 UPDATE 卡住,查 data_lock_waits 却没记录,但 data_locks 里有大量锁。这很可能是锁升级(Lock Escalation)——InnoDB 在扫描大量行后,把行锁升级为意向锁或表锁,导致后续 DML 被整体阻塞,但不产生传统意义上的“等待锁”关系。
- 典型场景:未走索引的 UPDATE 或 DELETE,全表扫描 → 大量行锁 → 触发锁升级条件(如超过 2000 行)→ 持有表级意向排他锁(
IX),其他 DML 就会排队,但data_lock_waits不记录这种“隐式阻塞” - 判断方法:查
data_locks中同一THREAD_ID下的锁数量,如果远超实际修改行数(比如锁了 5000 行,但 SQL 只改 10 行),基本就是扫描引发的锁膨胀 - 解决不在锁追踪本身,而在优化 SQL:加索引、缩小 WHERE 范围、拆分大事务
锁细节藏得深,LOCK_DATA 的格式、THREAD_ID 的映射、锁升级的静默阻塞——这些地方不手动验证一遍,很容易以为“没锁”或“锁错了”。










