innodb_lock_wait_timeout仅控制本地事务锁等待超时(推荐10~25秒),不能防范分布式死锁;真正需排查持锁事务、缺失索引、事务粒度及隔离级别等问题。

innodb_lock_wait_timeout 不是用来“防死锁”或“解决锁竞争”的,它只决定事务在本实例内等锁最多忍多久——设错值反而会让问题更难排查、更易雪崩。
看到 Lock wait timeout exceeded 错误,先别急着改这个参数
这个报错(ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction)本质是症状,不是病因。它只说明:当前事务在等某把 InnoDB 行锁/间隙锁,等了太久被强制踢出。真正要查的是谁在持锁、持了多久、为什么还不放。
- 运行
SELECT * FROM information_schema.INNODB_TRX,重点关注TRX_STARTED和TRX_QUERY,找出运行超 60 秒的事务 - 再查
SELECT * FROM information_schema.INNODB_LOCK_WAITS,关联BLOCKING_TRX_ID和REQUESTING_TRX_ID,定位等待链 - 避免用
SHOW PROCESSLIST—— 它看不到 InnoDB 层的锁等待关系,容易误判
OLTP 场景下推荐设为 10~25 秒,而不是拍脑袋填个 30 或 60
高并发短事务(如订单创建、支付回调)的核心诉求不是“让我多等会儿”,而是“让锁快点释放”。过长的等待只会把线程池拖垮,前端早超时了,后端还在干等。
- 无重试机制的应用,建议设为
15;有 Spring Retry + 幂等保障的,可放宽到25 - 绝对不要设为
0(无限等待)或300(5 分钟)——前者极易引发连接堆积,后者掩盖真实问题 - 设太小(比如
5)会导致网络抖动、临时 IO 延迟都触发误超时,线上报错飙升但锁争抢一点没少
配置方式选错,等于没配
SET GLOBAL innodb_lock_wait_timeout = 15 看似生效了,但已存在的连接仍用旧值。应用连接池不重启,90% 的请求根本不会走新配置。
- 调试单条 SQL:用
SET SESSION innodb_lock_wait_timeout = 15,仅当前连接有效 - 长期稳定生效:必须写进配置文件(如
/etc/my.cnf的[mysqld]段),再重启 MySQL 或执行mysqladmin reload - 批量作业需放宽?单独为该连接设:
SET SESSION innodb_lock_wait_timeout = 600,然后执行大事务,切勿全局拉高
比调参重要十倍的三件事
参数只是调节“痛感”的刻度盘。真正要盯紧的,是锁从哪来、怎么来的、为什么迟迟不走:
- 检查执行计划里有没有
type: ALL—— 缺失索引导致 UPDATE 全表扫描,锁范围直接升级成表级 - 事务里混了 HTTP 调用、sleep、文件读写?这些操作让锁持有时间完全不可控
- 隔离级别是不是还卡在
REPEATABLE-READ?换成READ-COMMITTED能大幅减少间隙锁,尤其在范围 UPDATE 场景
最常被忽略的一点:innodb_lock_wait_timeout 对跨库、跨服务、跨网络的“分布式死锁”完全无效。你看到的“卡死”,大概率是本地长事务没提交,或者上游服务在等下游 MySQL 的行锁——问题始终在单实例内,只是链路变长了而已。











