innodb_deadlock_detect必须保持on,关闭会导致死锁被动超时(数十秒卡顿)、失去latest detected deadlock日志;innodb_lock_wait_timeout宜设为10秒并用set persist永久生效;innodb_print_all_deadlocks须写入配置文件重启才真正启用。

innodb_deadlock_detect 必须保持 ON,关掉等于自废武功
MySQL 5.5 起就默认开启 innodb_deadlock_detect,InnoDB 用 wait-for graph 实时构建锁等待图,几毫秒内就能识别循环等待并回滚代价小的事务。你看到的 ERROR 1213 (40001): Deadlock found when trying to get lock 就是它在正常工作。
关闭它(SET GLOBAL innodb_deadlock_detect = OFF)只适合极少数场景:已确认所有事务严格按相同顺序访问表,且 CPU 已成为瓶颈(检测图构建本身要消耗 CPU)。但现实中几乎不存在这种理想条件。
关掉后,死锁不再被主动发现,只能等 innodb_lock_wait_timeout 被动超时——用户感知是“卡住几十秒才失败”,比快速报错更糟;SHOW ENGINE INNODB STATUS 中的 LATEST DETECTED DEADLOCK 段也会消失,排查失去关键线索。
innodb_lock_wait_timeout 设为 10 秒,不是越小越好
这个参数只控制事务等待某一行被释放的最长时间,和死锁检测完全无关。改它不会影响 ERROR 1213 的触发时机,但会影响普通锁等待的响应速度。
- 默认 50 秒太长:一个事务卡住半分钟,连接池可能已耗尽,HTTP 请求早已超时断开
- 设太小(比如 1 秒)也不行:短时竞争会被误判为超时,应用层重试压力陡增
- 合理值取决于业务事务平均耗时:若
UPDATE/DELETE大多在 2–3 秒内完成,innodb_lock_wait_timeout = 10是较稳的选择 - 必须用
SET PERSIST innodb_lock_wait_timeout = 10永久生效,仅SET SESSION容易被连接池复用覆盖
innodb_print_all_deadlocks 必须写进配置文件重启才真生效
动态执行 SET GLOBAL innodb_print_all_deadlocks = ON 只对新连接临时有效,且 MySQL 重启即丢失。生产环境几乎不会频繁新建连接来继承这个值,所以等于没开。
真正起效的唯一方式是:
- 编辑
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf,在[mysqld]段下添加:innodb_print_all_deadlocks = ON - 执行
systemctl restart mysql - 验证是否生效:
SELECT @@innodb_print_all_deadlocks;返回ON才算成功
日志输出到 log_error 指定的错误日志文件(执行 SHOW VARIABLES LIKE 'log_error' 查路径),需确保 MySQL 进程对该路径有写权限,且 log_error_verbosity ≥ 3,否则死锁事件会被过滤掉。
高并发写入场景下,光调参数不够,必须配合事务隔离与索引优化
死锁不是数据库故障,而是并发逻辑缺陷的信号。参数只是兜底手段,根因往往在 SQL 层面:
- 把默认隔离级别从
REPEATABLE-READ改为READ-COMMITTED,能显著减少间隙锁(gap lock)范围,降低交叉冲突概率 - 开启
innodb_locks_unsafe_for_binlog = ON(仅对 RC 生效),进一步禁用间隙锁 - 所有写操作前务必
EXPLAIN验证是否走索引:type不能是ALL或index,key不能为NULL,rows不能远超预期 - 避免在 WHERE 条件中使用函数、隐式类型转换、非索引列 OR 等导致索引失效的操作
最常被忽略的一点:死锁日志里出现大量 lock_mode X locks gap before rec,说明间隙锁正在失控——这往往不是参数问题,而是缺少合适索引或事务访问顺序不一致造成的。











