SHOW ENGINE INNODB STATUS可实时获取最近一次死锁快照,重点查看LATEST DETECTED DEADLOCK段中的两个事务SQL、WAITING FOR THIS LOCK和HOLDS THE LOCK(S)信息;需配合innodb_print_all_deadlocks=ON持久化全量死锁日志,并用INFORMATION_SCHEMA.INNODB_LOCK_WAITS查实时锁等待链,应用层须捕获错误码1213并幂等重试。
MySQL死锁无法避免,但能快速定位、精准归因、稳定恢复——关键不是“怎么不发生”,而是“怎么秒级抓到现场并让应用不崩”。
这是最直接、最可靠的实时死锁快照,不需要日志轮转或配置重启,执行即得最近一次死锁详情。
重点看输出中 LATEST DETECTED DEADLOCK 区块,它包含:
TRANSACTION ID 和完整 SQL(比如 UPDATE orders SET status='shipped' WHERE order_id=1001)WAITING FOR THIS LOCK TO BE GRANTED 行明确指出等待的索引、页号、主键值HOLDS THE LOCK(S) 对应的 RECORD LOCKS 描述了锁定范围WE ROLL BACK TRANSACTION (1))注意:该命令只返回最近一次死锁。如果频繁发生,仅靠它会漏掉中间发生的多次死锁。
靠 innodb_print_all_deadlocks = ON 把所有死锁写进错误日志,这是生产环境必须开的开关。
操作分两步:
SET GLOBAL innodb_print_all_deadlocks = 1;
my.cnf 的 [mysqld] 段落添加:innodb_print_all_deadlocks = 1,然后重启 MySQL开启后,每次死锁都会在 log_error 指定路径(如 /var/log/mysql/error.log)追加一段完整上下文,包括时间戳、线程ID、SQL、锁结构——比 SHOW ENGINE INNODB STATUS 更全、更可追溯。
风险提示:高并发下死锁频发时,日志量可能陡增,需配合日志轮转策略,但绝不应因此关闭该选项。
当死锁正在发生(还没被检测到或刚发生),或者你想确认是否存在隐性阻塞链时,用 information_schema.INNODB_LOCK_WAITS 关联查询最有效。
标准查询模板:
SELECT r.trx_id AS waiting_trx_id, r.trx_mysql_thread_id AS waiting_thread, r.trx_query AS waiting_query, b.trx_id AS blocking_trx_id, b.trx_mysql_thread_id AS blocking_thread, b.trx_query AS blocking_queryFROM information_schema.INNODB_LOCK_WAITS wJOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_idJOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;
结果直接告诉你:哪个线程(waiting_thread)卡在哪条 SQL(waiting_query),被哪个线程(blocking_thread)的哪条 SQL(blocking_query)挡住。
这个查询不依赖死锁是否已触发,只要存在锁等待就可查出,是排查“疑似死锁卡顿”的第一手证据。
MySQL 返回的错误码是 ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction,应用必须捕获并重试,不能当作普通异常抛出。
重试逻辑要点:
UPDATE、INSERT ... ON DUPLICATE KEY UPDATE),非幂等操作(如无条件 INSERT)重试可能引发重复数据Python 示例中捕获 e.errno == 1213 就是标准做法;Java 用 SQLException.getSQLState().equals("40001") 判断。
真正容易被忽略的是:重试必须在同一个数据库连接内完成,否则新连接可能拿到旧事务未提交的脏状态,导致逻辑错乱。