innodb_lock_wait_timeout必须设置,它仅控制事务等待行锁的最长时间,默认50秒过长易致连接池耗尽,设为10秒较稳妥;需用SET PERSIST永久生效,且不可关闭默认开启的死锁检测机制。

行锁超时时间必须设,死锁检测机制默认已开、不该关——调错参数或关掉检测,反而让问题更难察觉、更难恢复。
innodb_lock_wait_timeout 怎么设才不误事
这个参数只管「事务等待某一行被释放」的最长时间,和死锁检测完全无关。你改它,不会影响 Deadlock found 错误的触发时机,但会影响普通锁等待的响应速度。
- 默认 50 秒太长:一个事务卡住半分钟,连接池可能已耗尽,HTTP 请求早已超时断开
- 设太小(比如 1 秒)也不行:短时竞争会被误判为超时,应用层重试压力陡增
- 合理值取决于业务事务平均耗时:若 UPDATE/DELETE 大多在 2–3 秒内完成,设为
innodb_lock_wait_timeout = 10是较稳的选择 - 必须用
SET PERSIST永久生效:MySQL 8.0 支持该命令,执行后立即内存生效 + 写入mysqld-auto.cnf,重启不丢失 - 别只改 session 级:应用连接池复用连接,
SET SESSION innodb_lock_wait_timeout = 5很容易被后续语句覆盖或遗忘
innodb_deadlock_detect 不用开、也别关
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超时被动回滚——用户感知是“卡住几十秒才失败”,比快速报错更糟 - MySQL 8.0.22+ 才支持运行时 SET,旧版本必须重启;且关闭后
SHOW ENGINE INNODB STATUS中的LATEST DETECTED DEADLOCK段会消失,排查失去关键线索
死锁日志怎么留全、找得到
innodb_print_all_deadlocks 必须写进配置文件并重启 MySQL 才真正启用,动态 SET GLOBAL 只对新连接临时有效,生产环境几乎无效。
- 编辑
/etc/my.cnf或/etc/mysql/mysql.conf.d/mysqld.cnf,在[mysqld]段下加:innodb_print_all_deadlocks = ON - 确认错误日志路径:
SHOW VARIABLES LIKE 'log_error',常见路径如/var/log/mysql/error.log - 确保 MySQL 进程对该路径有写权限:
ls -ld $(dirname $(mysql -Nse "SELECT @@log_error")) - 日志级别至少为 3:
log_error_verbosity = 3(否则死锁块可能被过滤) - 监听时加
--line-buffered:tail -f /var/log/mysql/error.log | grep --line-buffered -i "TRANSACTION\|deadlock",否则容易漏掉实时块
DDL 卡住别乱调 innodb_lock_wait_timeout
ALTER TABLE、CREATE INDEX 卡在 Waiting for table metadata lock,不是行锁问题,而是元数据锁(MDL)阻塞。innodb_lock_wait_timeout 对它完全无效。
- 真正该调的是
lock_wait_timeout,默认 31536000 秒(一年),建议设为60并用SET PERSIST lock_wait_timeout = 60持久化 - 但调它只是兜底:DDL 卡住的根因,90% 是有长事务未提交、慢查询没结束,或客户端连接假死(
SHOW PROCESSLIST中Command = Sleep且Time很大) - 先查源头:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60 - 再看谁在等 MDL:
SELECT * FROM performance_schema.metadata_locks ml JOIN performance_schema.threads t ON ml.OWNER_THREAD_ID = t.THREAD_ID WHERE ml.LOCK_STATUS = 'PENDING'
死锁本身不可怕,可怕的是日志没留全、参数调错方向、把 MDL 问题当成行锁问题去调。最常被忽略的点是:innodb_print_all_deadlocks 动态设置等于白设,以及 lock_wait_timeout 和 innodb_lock_wait_timeout 混用——它们作用的对象完全不同,写错配置文件或执行错命令,问题只会藏得更深。


















