InnoDB 死锁日志里那一段,到底该怎么读

举报
yd_228692151 发表于 2026/10/26 11:29:31 2026/10/26
【摘要】 从 SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 出发,说清楚 TRANSACTION 块、HOLDS/WAITS 和 lock_mode 里的 index 信息该怎么看,以及哪些情况其实是加锁顺序问题而不是隔离级别问题。

出现死锁之后第一件事是执行 SHOW ENGINE INNODB STATUS,输出里 LATEST DETECTED DEADLOCK 那一段记录的是最近一次死锁。默认只留最近一次,如果想让每次死锁都落到错误日志里,需要把 innodb_print_all_deadlocks 打开。

那段日志大概长这样,表名和事务号是我自己测试表上的:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2026-10-20 14:22:07 0x7f8a2c0b1700
*** (1) TRANSACTION:
TRANSACTION 48213, ACTIVE 4 sec starting index read
mysql tables in use 2, locked 2
LOCK WAIT 4 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 912, OS thread handle 1395, query id 77213 10.0.0.12 app updating
UPDATE t_order SET status = 2 WHERE user_id = 10086 AND status = 1
*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 231 page no 17 n bits 96 index idx_user_status of table `shop`.`t_order` trx id 48213 lock_mode X
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 231 page no 19 n bits 96 index idx_user_status of table `shop`.`t_order` trx id 48213 lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
TRANSACTION 48214, ACTIVE 4 sec starting index read
...
*** WE ROLL BACK TRANSACTION (2)

读的时候我一般只看三处。

第一处是每个事务块开头那行 SQL。它给的是这个事务当前卡住的那条语句,不是事务里执行过的全部语句,所以别拿它当完整执行序列用。

第二处是 HOLDS THE LOCK(S) 和 WAITING FOR THIS LOCK TO BE GRANTED。前者是已经拿到的锁,后者是正在等的锁。两个事务互相出现在对方的 HOLDS 里,就是标准的交叉等待。这一段里还带着 page no、heap no 这些物理位置信息,定位具体哪一行靠它们。

第三处是锁描述里的 index 名字和 lock_mode。index 说明这次加锁落在哪个索引上。如果这里出现 PRIMARY,而你原本以为会走二级索引,那加锁范围往往比预期大得多。lock_mode 里 X locks rec but not gap 是记录锁,带 gap 字样的是间隙锁;如果看到 supremum pseudo-record,说明锁到了索引末尾之后的位置,一般来自范围条件或者扫描范围过大。

有个常见的误判是把它当成隔离级别问题,直接去改 transaction_isolation。改成 READ COMMITTED 确实能消掉大部分间隙锁,死锁概率会下降,但如果根因是加锁顺序相反,那只是把冲突概率压低,并没有解决。日志里如果两个事务的 index 相同、加锁的行集合也相同,但先后顺序不一样,那就是顺序问题,跟隔离级别无关。

顺序问题在批量更新里最典型。两条 UPDATE 分别按 id 升序和降序扫同一批行,各拿一半锁之后互等。处理办法是在应用层把待更新主键排序后再逐条执行,或者用一条带 IN 的语句让 MySQL 自己按索引顺序加锁。后者的代价是锁范围可能变大,要看具体数据量。

还有一种情况是唯一索引冲突引起的死锁,INSERT ... ON DUPLICATE KEY UPDATE 在高并发下很容易触发,日志里的 lock_mode 会带 insert intention 或者表现为对同一唯一键的 S 锁等待。这类不能靠调整顺序解决,通常要改成先 SELECT 再决定 INSERT 还是 UPDATE,或者退化成 INSERT IGNORE 加一条 UPDATE。

最后提一下错误码。死锁被回滚的一方会收到 1213,Deadlock found when trying to get lock; try restarting transaction。这个错误必须在上层捕获并重试,重试次数有限、带退避,否则用户会直接看到报错。锁等待超时是 1205,两者处理方式不一样,别混在一起。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。