死锁检测默认开启,InnoDB通过wait-for graph实时检测并快速回滚事务;关闭它会导致依赖超时被动处理,体验更差;应优先调整innodb_lock_wait_timeout和innodb_print_all_deadlocks,并规范SQL表访问顺序。

死锁检测默认是开启的,不用手动打开
MySQL 的 InnoDB 引擎从 5.5 开始就默认启用死锁检测(innodb_deadlock_detect),只要事务涉及行锁且出现循环等待,InnoDB 就会在几毫秒内识别并主动回滚其中一个事务——你看到的 Deadlock found when trying to get lock 错误,就是它在工作的证据。这个机制不依赖定时轮询,而是基于 wait-for graph 实时构建和检测,所以响应很快。
常见错误现象:ERROR 1213 (40001): Deadlock found when trying to get lock 不代表系统坏了,恰恰说明检测机制在起作用;如果长时间没报错但请求卡住,反而要怀疑是不是锁超时太长、或者用了 SELECT ... LOCK IN SHARE MODE 却没提交。
想关掉死锁检测?真没必要,而且有代价
虽然可以通过 SET GLOBAL innodb_deadlock_detect = OFF 关闭,但这么做只适合极少数场景:比如已确认所有事务都是严格按相同顺序访问表(如先 orders 再 order_items),且并发量极大、CPU 成为瓶颈(因为检测本身要维护锁等待图)。关闭后,InnoDB 只能靠 innodb_lock_wait_timeout(默认 50 秒)被动超时回滚,用户会感知到明显卡顿,而不是快速失败重试。
-
innodb_deadlock_detect = OFF后,死锁不会被立即发现,得等锁超时才释放,体验更差 - 该变量是只读动态变量,MySQL 8.0.22+ 才支持运行时 SET,旧版本必须重启生效
- 关闭后,
SHOW ENGINE INNODB STATUS中的LATEST DETECTED DEADLOCK段会消失
真正该调的两个参数:超时与日志
比起开关死锁检测,更值得调整的是让系统“更快失败”和“更容易定位”。重点看这两个配置:
-
innodb_lock_wait_timeout:控制等待锁的最长时间,默认 50 秒。高并发写场景建议设为 5–10 秒,避免一个慢事务拖垮整个连接池 -
innodb_print_all_deadlocks:设为ON,能把每次死锁详情写进 error log(不只是最近一次),方便事后分析谁锁了谁、哪条 SQL 先抢的资源
示例:临时修改当前会话超时时间
SET SESSION innodb_lock_wait_timeout = 5;
死锁不是配置问题,是 SQL 访问顺序问题
95% 的死锁源于事务中多表操作顺序不一致,比如事务 A 先更新 users 再更新 profiles,而事务 B 反过来。死锁检测只是兜底手段,治本得靠代码规范:
- 所有涉及多表更新的业务逻辑,强制约定表访问顺序(按字母序、或按主外键依赖关系)
- 避免在事务里做 RPC、文件读写、用户输入等不可控延迟操作
- 用
SELECT ... FOR UPDATE时,尽量加WHERE条件走索引,否则可能升级为表级锁,扩大冲突面
容易被忽略的一点:即使开了死锁检测,如果事务持有锁的时间过长(比如大事务、批量更新没分页),冲突概率仍会指数上升——检测再快,也架不住锁堆在一起。










