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;

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

相关推荐
wxwx_bscxy32211 小时前
基于springboot宠物领养系统的设计与实现
数据库·spring boot·后端·spring·宠物
九月要晚安12 小时前
达梦DW与AFC对比
数据库
实战派K8S&DB12 小时前
如何在内网配置 TiDB 数据库 Agent
数据库·人工智能·分布式·tidb
实战派K8S&DB12 小时前
《基于 Dify + FastAPI + PyTiDB 搭建大模型驱动的 TiDB 智能运维 Agent》
运维·数据库·分布式·云原生·tidb·fastapi
SelectDB12 小时前
Apache Doris 5.0 年度版本前瞻(一):构建统一的多模湖仓实时分析平台
大数据·数据库·数据分析
小白学大数据13 小时前
超简单:用 Python 让 Excel 飞起来:用 openpyxl 把重复报表整理交给脚本
开发语言·数据库·python·excel
西索斯coding13 小时前
doubao-seed-2.1-turbo 调用一直 401 怎么办?pro 版同样的 Key 却正常——5 分钟排查定位指南
java·服务器·数据库·ai
袋鼠云数栈13 小时前
整库迁移与分库分表同步:批量数据搬迁的配置简化思路
jvm·数据库·oracle
小蒜学长14 小时前
基于Springboot+Vue的环保行动志愿者招募系统设计与实现(代码+数据库+LW)
java·数据库·vue.js·spring boot·后端
cfm_291414 小时前
Spring事务
数据库·spring·oracle