欢迎光临
我们一直在努力

5.6 InnoDB死锁

死锁(Deadlock)是多个事务因循环等待锁资源而永久阻塞的现象。它虽然危险,但并不可怕,MySQL InnoDB 有自动检测和恢复机制。

🚨 死锁是如何产生的?

死锁的产生必须同时满足四个条件,InnoDB 中的死锁通常源于以下两种典型场景:

  • 不同表,相反顺序:事务A更新表1再更新表2,事务B更新表2再更新表1,形成循环等待。
  • 相同表,不同范围:事务A通过范围条件锁定一个间隙,事务B锁定另一个间隙,随后各自尝试插入对方锁定的间隙,导致死锁。
  • 注意:死锁主要由写操作(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 语句和锁资源,找出循环等待的路径。通常根因是事务以不一致的顺序访问资源,或锁定的范围有重叠。

  • 🛡️ 如何预防死锁?

    预防优于处理,以下是最佳实践:

  • 强制统一访问顺序:所有事务按相同顺序(如主键升序)访问表和行。
  • 保持事务短小精悍:尽快提交,减少锁持有时间。
  • 使用合适的索引:确保 WHERE 条件能命中索引,避免行锁升级为表锁。
  • 考虑降低隔离级别:若业务允许,使用 READ COMMITTED 级别可减少间隙锁,降低死锁概率。
  • 使用 INSERT … ON DUPLICATE KEY UPDATE:合并先查后改的逻辑,减少锁请求次数。
  • 🏥 如何从死锁中恢复?

    死锁是并发场景下的常态,需要在应用层优雅处理。

    • 捕获并重试:这是最核心的恢复策略。在代码中捕获死锁异常(Deadlock found when trying to get lock; try restarting transaction),并进行有限次数的重试(通常1-2次即可成功)。

    💎 总结

    • 死锁本质:多个事务因循环等待锁而无限阻塞。
    • InnoDB策略:默认自动检测并回滚“代价最小”的事务。
    • 核心对策:统一资源访问顺序 + 缩短事务 + 应用层重试。
    • 不要害怕:死锁是并发数据库的正常现象,设计健壮的重试机制是解决问题的关键。
    赞(0)
    未经允许不得转载:171主机测评 » 5.6 InnoDB死锁
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址