MySQL 死锁怎么处理:等待图、锁顺序和事务收口

1次阅读
没有评论

死锁不是数据库“坏了”,而是并发事务互相等待对方释放资源,形成了环。理解死锁的条件和处理方式,有助于写出更稳定的事务代码。

活锁和死锁不是一回事

活锁是某个事务一直得不到机会。例如多个事务不断插队获取同一资源,某个早来的事务一直等待。解决思路通常是公平队列或先来先服务。

死锁是多个事务互相等待。事务 A 锁住资源 1,等待资源 2;事务 B 锁住资源 2,等待资源 1,双方都无法继续。

活锁是“总有人在动,但你一直轮不到”;死锁是“大家都卡住了”。

死锁产生需要多个条件

经典条件包括:

  • 互斥:资源一次只能被一个事务持有。
  • 持有并等待:持有一个资源时继续申请另一个资源。
  • 不可抢占:资源不能被外部强行拿走,只能持有者释放。
  • 循环等待:多个事务形成等待环。

理论上破坏任意一个条件都能预防死锁,但数据库系统里完全预防通常会牺牲太多并发能力。

数据库通常靠检测和回滚解决

数据库一般不要求所有事务一次性申请全部锁,也很难强制所有业务按固定资源顺序加锁。因此更常见的是检测死锁并选择一个代价较小的事务回滚。

检测方式包括超时和等待图。等待图把事务作为节点,把“谁等待谁”作为边。如果图里出现环,就说明发生死锁。

InnoDB 检测到死锁后,会回滚其中一个事务,让其他事务继续执行。

业务代码要减少死锁概率

虽然数据库能检测死锁,但业务不能完全依赖数据库兜底。常见优化手段有:

  • 多个事务按相同顺序访问表和行。
  • 更新前确保 where 条件命中索引,避免锁范围扩大。
  • 事务里只做必要数据库操作,不夹杂远程调用和耗时计算。
  • 批量更新拆小批,减少持锁时间。
  • 对可重试操作增加幂等和有限重试。

锁的范围越小,持锁时间越短,死锁概率越低。

排查要看最近一次死锁信息

MySQL 可以通过 InnoDB 状态查看最近一次死锁信息,重点看:

  • 哪两个事务参与死锁。
  • 各自执行的 SQL。
  • 持有哪些锁,等待哪些锁。
  • 是否走了索引。
  • 锁住的是记录锁、间隙锁还是范围。

不要只看“Deadlock found”这行异常。真正的原因在事务持锁顺序和 SQL 条件里。

重试不是万能药

死锁异常通常可以重试,但重试必须有边界。

适合重试的前提是操作幂等、事务短、失败概率低。不能无限重试,也不能把死锁当成正常高频路径。重试只能降低偶发死锁影响,不能替代 SQL 和事务设计。

实用结论

MySQL 死锁处理要从三个层面看:数据库会检测等待环并回滚一个事务;业务要统一加锁顺序、缩短事务、控制锁范围;应用层可以对幂等操作做有限重试。

死锁不是靠“加大超时时间”解决的。真正有效的改法,通常是改访问顺序、补索引、缩短事务和减少批量锁竞争。

正文完
 0
bdspAdmin
版权声明:本站原创文章,由 bdspAdmin 于2026-07-05发表,共计1044字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)