innodb_lock_wait_timeout仅控制行锁等待超时(默认50秒),不解决锁持有过长问题;设太小致频繁报错,设太大掩盖隐患;需配合查锁、优化事务、加索引等根治。

别直接改全局值到 30 或 60 秒——它治标不治本,还可能掩盖真正的锁持有过长问题。
为什么改 innodb_lock_wait_timeout 容易踩坑
这个参数只决定“等多久就放弃”,不解决“为什么别人一直不放锁”。设太小(比如 10 秒),正常短事务被中断,报错 ERROR 1205 (HY000): Lock wait timeout exceeded 频发;设太大(比如 300 秒),用户请求卡住五分钟才失败,前端早超时了,后端还在干等。
更关键的是:SET GLOBAL innodb_lock_wait_timeout = 30 只对新连接生效,已连上的旧连接仍用原值,线上服务 reload 配置也不触发重连,所以你以为改了,其实没起效。
- 默认值是 50 秒(MySQL 5.7+),不是 50ms(网上有误传)
- 会话级设置
SET SESSION innodb_lock_wait_timeout = 60只影响当前连接,适合调试,但不能替代架构优化 - 永久生效必须写进
my.cnf的[mysqld]段,且需重启或mysqladmin reload(部分版本支持)
OLTP 场景下怎么设才合理
高并发、短事务的业务(比如订单创建、支付回调),重点不是“让我多等会儿”,而是“让锁快点释放”。此时建议把 innodb_lock_wait_timeout 压到 10–30 秒,并配合其他动作:
- 优先检查并 kill 掉长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - TRX_STARTED) > 60 - 确认是否用了
READ-COMMITTED隔离级别——它不加间隙锁,能显著减少锁范围,尤其在范围 UPDATE 场景 - 避免在事务里做 HTTP 调用、文件读写、sleep 等非数据库操作,这类事务锁持有时间不可控
- 检查是否有缺失索引导致 UPDATE/DELETE 扫全表,把行锁升级成表级锁争抢
批量作业和报表类任务要不要调大
要,但得加条件。报表查询本身不写数据,一般不会持锁,真正需要放宽的是那些含大事务的批量更新(比如日终跑批)。这时可单独为该连接设高值:
SET SESSION innodb_lock_wait_timeout = 600; -- 然后执行你的 UPDATE ... LIMIT 10000 循环
注意两点:
- 不要全局设 600,否则所有普通请求都跟着陪绑
- 搭配
innodb_rollback_on_timeout = ON,确保超时后自动回滚,避免留下半开事务 - 如果作业本身耗时超过 600 秒,说明逻辑有问题——应拆成小事务 + 显式 commit,而不是靠拉长等待时间硬扛
查锁和定位真凶比调参更重要
遇到 Lock wait timeout exceeded; try restarting transaction,第一反应不该是改参数,而是立刻查谁在 hold 锁:
- 运行
SHOW ENGINE INNODB STATUS\G,看TRANSACTIONS和LOCK WAIT段,找出 blocking trx_id - 查
sys.innodb_lock_waits视图(需启用 performance_schema),它把锁等待关系扁平化了 - 用
SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep'找长时间 Running 的线程,再用KILL {id}干掉
真正难调的从来不是这个 timeout 值,而是那些没 commit 的事务、没走索引的 UPDATE、跨库事务里混着 Redis 调用——这些藏在应用层的锁持有源头,才是超时错误反复出现的根本原因。











