mysql通过show engine innodb status\g查看最近死锁详情,关键看latest detected deadlock中两个事务的sql、持有/等待锁状态及被回滚事务;开启innodb_print_all_deadlocks=on可持久化所有死锁日志便于回溯;根治需统一加锁顺序、缩短事务、应用层重试,而非仅调参或改隔离级别。

MySQL 会自动回滚一个事务来打破死锁,但业务层不捕获 Deadlock found when trying to get lock 并重试,就会直接报错;不分析 SHOW ENGINE INNODB STATUS 中的 LATEST DETECTED DEADLOCK,就永远不知道哪两条 SQL 在掐架。
怎么立刻看到最近一次死锁详情
执行 SHOW ENGINE INNODB STATUS\G 是最直接、无需额外配置的方法。输出中唯一值得关注的部分就是 LATEST DETECTED DEADLOCK 区域,它包含:
- 两个事务各自的
trx_id和正在执行的 SQL(注意看query id和UPDATE/SELECT ... FOR UPDATE内容) - 谁在等什么锁:比如
WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS ... lock_mode X - 谁拿着什么锁:
HOLDS THE LOCK(S): ... lock mode S或lock_mode X locks rec but not gap - 被 MySQL 主动回滚的是哪个事务(通常标为
*** (1) TRANSACTION或*** (2) TRANSACTION后面带ROLLBACK提示)
别跳过“持有锁”和“等待锁”的对比——这直接暴露了循环等待链。例如事务 A 持有 users 行锁却等 orders 行锁,事务 B 反过来,就是典型的交叉更新死锁。
为什么 innodb_print_all_deadlocks = ON 很关键
默认 MySQL 只保留最后一次死锁日志,线上偶发死锁一旦没及时查,就彻底丢失现场。开启该参数后,所有死锁都会追加写入错误日志(路径由 log_error 配置),方便事后回溯。
- 临时生效:
SET GLOBAL innodb_print_all_deadlocks = ON; - 永久生效:在
my.cnf的[mysqld]下添加innodb_print_all_deadlocks = 1 - 配合
tail -f /var/log/mysql/error.log | grep -i deadlock可实时告警
不配这个,等于让数据库“打死不认账”,只留一次快照,排查成功率断崖式下降。
如何从日志定位到具体代码行
死锁日志里的 SQL 通常是简化后的最终执行语句,但业务代码中往往包裹着事务模板、重试逻辑或 ORM 封装。要对齐,得盯住三个锚点:
-
trx_mysql_thread_id:对应information_schema.INNODB_TRX中的线程 ID,可关联应用日志中的请求 traceID(如果打了 DB 线程 ID 标签) - SQL 中的常量值:比如
WHERE user_id = 12345,搜索代码里所有出现12345或变量名含userId的事务方法 - 表操作顺序:日志里事务 A 先更新
orders再更新inventory,就去翻代码里所有涉及这两张表的事务,看有没有反序调用
ORM 如 MyBatis 的 @Transactional 方法名、JDBC 的 Connection 获取位置,都是顺藤摸瓜的关键线索。光看 SQL 不翻代码,永远只能修表面。
哪些改法真正有效,哪些只是自我安慰
很多方案听起来合理,但实际效果差异极大:
- ✅ 统一加锁顺序:所有事务严格按
users → orders → inventory顺序操作,是根治交叉更新死锁的唯一可靠手段 - ✅ 缩短事务:把远程调用、复杂计算、日志打印移出
BEGIN...COMMIT,减少锁持有时间 - ✅ 应用层重试:捕获
SQLException中的 SQLState40001或 message 含Deadlock,自动重试 1–3 次(需幂等) - ❌ 单纯调大
innodb_lock_wait_timeout:只会把死锁延迟成锁超时,阻塞更久,不解决根本 - ❌ 改隔离级别到
READ COMMITTED:虽能禁用间隙锁,但可能引发幻读,且对主键等值更新类死锁无效 - ❌ 加索引却不验证执行计划:
EXPLAIN显示仍走全表扫描,行锁照样升表锁,死锁照旧
真正卡点在于:开发往往只改 SQL,不碰事务边界;DBA 只看日志,不问代码路径;运维只调参,不推重试机制。三者断开,死锁就永远在“又出现了”和“再看看”之间循环。











