
怎么看死锁日志里谁在等谁
MySQL死锁日志的核心逻辑就一句话:事务A持有X锁、等待Y锁,事务B持有Y锁、等待X锁——循环等待成立,就死锁了。关键不是看“谁错了”,而是从日志里把这两条链路串出来。
实操时盯紧三块内容:*** (1) TRANSACTION、*** (1) HOLDS THE LOCK(S)、*** (1) WAITING FOR THIS LOCK TO BE GRANTED。注意编号(1)和(2)是并列的两个事务,不是先后顺序。
-
trx_id和MySQL thread id要对应上,避免把两个不相干的事务误认成死锁对 -
lock_mode X locks rec but not gap表示行锁(没间隙),lock_mode X locks gap before rec才是间隙锁——RC隔离级别下也可能出现后者,别一看到gap就断定是RR级别问题 - 物理记录里的
heap no和PHYSICAL RECORD中的字段值(比如hex 0000000a; asc ;;对应十进制10)能帮你定位到具体哪一行,结合WHERE条件反推SQL意图
怎么让死锁日志自动落盘不丢
靠 SHOW ENGINE INNODB STATUS\G 只能看到最后一次死锁,线上出问题时根本来不及抓——必须让MySQL自己记下来。
启用方式很简单,但有两个参数必须配齐:
- 设
innodb_print_all_deadlocks = ON:这是开关,不开它啥都不记 - 设
log_error_verbosity = 3:否则死锁信息只写NOTE级别,可能被日志轮转策略过滤掉 - 注意:
innodb_print_all_deadlocks默认是OFF,且开启后错误日志会明显变大,建议搭配日志切割策略(如logrotate)使用,别让它撑爆磁盘
为什么用INFORMATION_SCHEMA查实时锁比看日志更快
日志是“事后录像”,而 INFORMATION_SCHEMA.INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS 是“现场直播”。当应用卡住、你怀疑正在发生死锁但还没报错时,这三张表就是救命稻草。
典型查询组合:
SELECT t.trx_id, t.trx_state, t.trx_started, t.trx_query,
w.requesting_trx_id, w.blocking_trx_id
FROM INFORMATION_SCHEMA.INNODB_TRX t
JOIN INFORMATION_SCHEMA.INNODB_LOCK_WAITS w
ON t.trx_id = w.requesting_trx_id;
-
trx_state = 'LOCK WAIT'的事务正在堵着,blocking_trx_id就是它的“拦路虎” -
trx_query字段可能为空(比如事务只开了没执行SQL),这时得结合trx_started和线程ID去SHOW PROCESSLIST补全上下文 - MySQL 8.0.1 开始
INNODB_LOCKS表被移除,改用performance_schema.data_locks,别在新版本还硬查老表
常见误判点:为什么“没加索引”和“间隙锁”总被冤枉
很多同学一看死锁日志里有 gap before rec 就立刻去改SQL加索引,结果问题还在——因为根本不是索引缺失导致的。
- RC级别下间隙锁依然存在,主要用于唯一键冲突检查和外键约束,哪怕你
WHERE条件走的是主键,插入时仍可能触发间隙锁竞争 -
INSERT ... SELECT或批量INSERT场景中,InnoDB会对目标区间预加插入意向锁(insert intention),它本质是gap lock的一种,但和其他gap lock兼容——所以别见了insert intention就以为是冲突源 - 真正该查索引缺失的信号是:
WAITING FOR THIS LOCK TO BE GRANTED明确指向一个非唯一辅助索引(比如index idx_c1),且trx_query里是UPDATE/DELETE带范围条件(如WHERE c1 > 10)
死锁从来不是单点问题,它是并发路径、SQL写法、索引结构、隔离级别四者咬合的结果。盯着日志里那一行WE ROLL BACK TRANSACTION (2)没用,得把事务从开始到卡住的每一步执行计划都串起来看。











