performance schema 无法查询历史死锁,因其 data_locks 和 data_lock_waits 仅保存当前活跃事务的瞬时锁信息,事务结束后记录即消失;唯一可靠方式是启用 innodb_print_all_deadlocks=on,依赖错误日志中持久化的死锁上下文。

Performance Schema 本身不记录历史死锁——它只提供实时锁状态,data_locks 和 data_lock_waits 是瞬时快照,事务提交或回滚后对应记录就消失了。所谓“定位历史死锁原因”,真正可依赖的只有 innodb_print_all_deadlocks = ON 写入错误日志的内容,Performance Schema 只能辅助还原当时上下文。
为什么不能从 performance_schema 查历史死锁
MySQL 的 performance_schema 是内存驻留的运行时监控框架,所有锁相关表(如 data_locks、data_lock_waits)只保留当前活跃事务的锁信息:
-
data_locks不保存已释放/已回滚事务的锁结构,查不到“昨天那个死锁里事务A到底锁了哪几行” -
data_lock_waits仅反映“此刻谁在等谁”,死锁被 InnoDB 检测并回滚后,等待关系立即清除 - 没有类似
history_deadlocks或归档表,官方未提供任何持久化历史死锁锁路径的机制
如何用 performance_schema 辅助分析死锁日志中的线索
当从错误日志里拿到一次死锁的原始输出(比如 SHOW ENGINE INNODB STATUS\G 中的 LATEST DETECTED DEADLOCK),可以用 performance_schema 实时验证关键假设:
- 确认涉及的线程是否仍在运行:
SELECT * FROM performance_schema.threads WHERE THREAD_ID = ?(用日志里的mysql thread id替换) - 查该线程最近执行的语句:
SELECT * FROM performance_schema.events_statements_current WHERE THREAD_ID = ? - 若事务尚未结束,用
THREAD_ID关联data_locks,看它实际持有的锁范围是否与日志中lock_mode X locks rec but not gap描述一致 - 特别注意日志里出现的
lock_data值(如PRIMARY, 123),在data_locks.LOCK_DATA中搜索是否匹配,验证是否真锁住了那条记录
真正能查历史死锁的唯一可靠方式
必须靠日志,不是表:
- 确保已启用:
SET GLOBAL innodb_print_all_deadlocks = ON(或写入my.cnf并重启) - 死锁发生时,完整上下文会追加到 MySQL 错误日志(
mysqld.err或系统日志),包含两个事务的 SQL、锁类型、索引名、具体锁定值、被选为牺牲者的判断依据 - 不要依赖
SHOW ENGINE INNODB STATUS——它只保留最后一次,且重启后清空 - 生产环境建议用日志轮转+定时采集(如
logrotate+grep -A 50 "LATEST DETECTED DEADLOCK")做归档分析
最容易被忽略的一点:死锁日志里显示的 lock_mode X locks gap before rec 往往比 rec but not gap 更危险——间隙锁的范围不可见、难预测,但 performance_schema.data_locks 中的 LOCK_DATA 字段对间隙锁可能为空或显示为 NULL,此时只能靠 EXPLAIN 和索引结构反推其影响范围。











