死锁(Deadlock)是多个事务因循环等待锁资源而永久阻塞的现象。它虽然危险,但并不可怕,MySQL InnoDB 有自动检测和恢复机制。
🚨 死锁是如何产生的?
死锁的产生必须同时满足四个条件,InnoDB 中的死锁通常源于以下两种典型场景:
注意:死锁主要由写操作(UPDATE, DELETE, INSERT, SELECT … FOR UPDATE)引起,其发生概率不受隔离级别直接影响。
🔍 MySQL InnoDB 如何处理死锁?
MySQL InnoDB 提供了自动处理机制,但在极端高并发下也可能需要人工干预。
- 自动检测与回滚:默认开启。InnoDB 会构建等待图(Wait-for Graph),检测到环路后,会回滚一个事务(“受害者”)来打破死锁。通常选择修改行数较少的事务作为回滚对象。
- 兜底超时机制:若禁用自动检测,或死锁涉及外部锁,则依赖 innodb_lock_wait_timeout(默认50秒)。事务等待超时后会自动回滚。
- 深度检测限制:当锁等待关系过于复杂(如超过200个事务),InnoDB 会直接回滚当前事务,避免性能崩溃。
🛠️ 如何排查死锁?
死锁排查的核心是获取和分析死锁日志。
获取死锁日志
- 查看最近一次死锁:执行 SHOW ENGINE INNODB STATUS\\G,在输出中查找 LATEST DETECTED DEADLOCK 部分。
- 记录所有死锁(推荐):开启 innodb_print_all_deadlocks = ON,将所有死锁记录到 MySQL 错误日志,便于长期追踪。
分析死锁日志 日志会清晰展示死锁的完整链条。
- 事务信息:TRANSACTION 块展示了事务ID和状态。
- 持有的锁:HOLDS THE LOCK(S) 部分显示该事务当前成功获取的锁。
- 等待的锁:WAITING FOR THIS LOCK 部分显示该事务被阻塞的锁请求。
- 回滚决策:日志末尾会标明哪个事务被回滚(WE ROLL BACK TRANSACTION)。
定位根因 分析日志中的 SQL 语句和锁资源,找出循环等待的路径。通常根因是事务以不一致的顺序访问资源,或锁定的范围有重叠。
🛡️ 如何预防死锁?
预防优于处理,以下是最佳实践:
🏥 如何从死锁中恢复?
死锁是并发场景下的常态,需要在应用层优雅处理。
- 捕获并重试:这是最核心的恢复策略。在代码中捕获死锁异常(Deadlock found when trying to get lock; try restarting transaction),并进行有限次数的重试(通常1-2次即可成功)。
💎 总结
- 死锁本质:多个事务因循环等待锁而无限阻塞。
- InnoDB策略:默认自动检测并回滚“代价最小”的事务。
- 核心对策:统一资源访问顺序 + 缩短事务 + 应用层重试。
- 不要害怕:死锁是并发数据库的正常现象,设计健壮的重试机制是解决问题的关键。





