MySQL InnoDB 如何检测死锁、判定死锁、处理死锁

MySQL InnoDB 如何检测死锁、判定死锁、处理死锁

环境:InnoDB引擎,死锁只发生在行锁等待,表锁MyISAM没有死锁检测。

一、核心:死锁检测算法 ------ 等待图(wait‑for graph)

InnoDB 内部维护一张锁等待图:

  • 节点:每一个事务
  • 边:事务A → 事务B,代表:A在等待B持有的锁

死锁判定逻辑:

在等待图中,如果存在环(循环回路) ,就判定发生死锁。

A等B,B等C,C等A,形成环路 → 死锁。

两个关键概念区分

  1. 锁等待 :A等B,无环,只是普通等待,不会报死锁,会等待直到锁释放,或者超过innodb_lock_wait_timeout(默认50秒)报锁等待超时。
  2. 死锁 :等待图出现环路,不是超时,是检测算法主动识别出来。

⚠️注意:死锁 ≠ 锁等待超时。超时是等太久拿不到锁;死锁是检测到循环等待,立刻处理。

二、检测时机

每当一个事务要等待某把锁的时候,InnoDB就触发死锁检测:

  1. 事务执行 update / delete / select for update,申请行锁;
  2. 锁被别的事务占有,本事务需要进入等待队列;
  3. 此时触发死锁检测,遍历等待图,检查有没有环路。

死锁检测会消耗CPU,并发极高场景,大量事务等待锁,检测会带来性能损耗。

参数 innodb_deadlock_detect 可以关闭死锁检测,关闭后不会识别死锁,死锁事务只会全部等待直到锁等待超时。

三、死锁发生后:InnoDB怎么做(牺牲策略)

检测出死锁环路之后,InnoDB不会让所有事务无限挂起,选择一个代价最小的事务作为牺牲者回滚:

选择标准:undo log回滚量最小的事务,回滚这个事务,打破等待环。

  1. 被选中回滚的事务:直接回滚,抛出死锁异常;
  2. 其他参与死锁的事务:继续执行,继续等待锁。

MySQL返回给应用层的异常

复制代码
Error 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

错误码:1213,SQLSTATE 40001 ,这就是死锁异常。

业务捕获这个异常,标准做法:做重试。
对比锁等待超时异常:

Error 1205: Lock wait timeout exceeded; try restarting transaction

错误码1205,是等待锁超时,不是死锁。

四、死锁日志记录

死锁发生时,最近一次死锁信息保存在内存中:

sql 复制代码
show engine innodb status;

找到 LATEST DETECTED DEADLOCK 块,里面打印:

  • 两个事务分别执行什么SQL
  • 各自持有什么锁、等待什么锁
  • 哪一个事务被选为牺牲者回滚

开启参数 innodb_print_all_deadlocks=ON,死锁信息同时写入MySQL错误日志,防止新死锁覆盖内存里的死锁记录。

注意:show engine innodb status 只保存最近一次死锁,新死锁会覆盖旧的。

五、关键点梳理

  1. 怎么判断死锁?

    InnoDB维护事务锁等待图,当事务申请锁发生等待时,遍历等待图,发现循环等待环路,判定死锁。

  2. 死锁是超时发现的吗?

    ❌不是。死锁是主动图检测发现 ;超时是参数innodb_lock_wait_timeout控制普通锁等待。就算时间很短,只要出现环路立刻判定死锁。

  3. 发生死锁后,全部事务回滚吗?

    ❌不会。只回滚代价最小的那一个事务,其余事务继续等待锁。

  4. 关闭死锁检测会怎样?

    innodb_deadlock_detect=off,不再做图检测;出现死锁时所有事务互相卡住,直到锁等待超时抛出1205错误。高并发热点行场景,有些公司会关闭检测,靠业务重试。

  5. 应用拿到什么异常?

    1213 Deadlock found when trying to get lock; try restarting transaction,业务捕获该异常进行重试。

六、简单流程总结

  1. T1持有锁A,等待锁B;T2持有锁B,等待锁A;
  2. T2申请锁A时,进入锁等待,触发死锁检测;
  3. 扫描等待图发现环路,判定死锁;
  4. 选undo量小的事务回滚;
  5. 被回滚的客户端收到1213死锁异常;
  6. 死锁现场写入innodb status日志。

补充:代码层面现象

Java中JDBC遇到死锁,会抛出SQLTransactionRollbackException,错误码1213;

业务代码捕获该异常,做有限次数重试,是标准处理方案。

相关推荐
叼烟扛炮2 小时前
Linux 学习笔记(五十六):IO 多路复用初探——select 的“三宗罪”
linux·服务器·数据库·笔记·学习·php
.痞老板.4 小时前
mysql初阶--数据库基础
数据库·mysql
城北徐宫5 小时前
MySQL约束条件一览
数据库·mysql
码农小河665 小时前
基于SSM+JSP+MySQL的在线医疗服务系统的设计与实现
java·开发语言·mysql
ClouGence7 小时前
数据库变更审批困局,被这个开源工具解决了
数据库·开源
TDengine (老段)8 小时前
TDengine IDMP 十大实用功能(9)
大数据·数据库·人工智能·物联网·时序数据库·tdengine·idmp
starrocks_stella9 小时前
EMR Serverless StarRocks Object Table:用 SQL 连接 OSS 文件与多模态 AI
数据库
这个DBA有点耶9 小时前
以为自增ID只是有空洞?聊聊双主架构下无声的“数据覆盖”
数据库·mysql·dba
陈布衣9 小时前
MySQL(1)_MySQL 5.7.21 Linux 安装笔记
linux·笔记·mysql
钱栈up10 小时前
内网全断,怎么核实高炉和烧结系统的数据库对接信息
数据库