合理值需按业务分级:金融类设5秒、普通web设15秒、离线任务设300秒;默认50秒在高并发下易放大故障,且必须配合innodb_rollback_on_timeout=on与索引优化等根治措施。

锁超时不是“等多久”,而是“该不该等”
调 innodb_lock_wait_timeout 不能减少锁争用,只决定事务在锁队列里站多久才放弃。设成 5 秒,不意味着系统变快了;它只是让失败更快暴露——这对有重试机制的 OLTP 服务(如订单创建)反而是好事。默认 50 秒在高并发下容易把几十个请求全拖进等待队列,最后一起超时,造成线程池耗尽或前端大面积报错。
会话级设置比全局修改更安全可控
线上改 SET GLOBAL innodb_lock_wait_timeout = 15 后,旧连接仍用原值,新连接才生效;而业务连接池通常长连接复用,实际效果可能延迟数小时甚至更久。更稳妥的做法是:在应用连接初始化时显式执行 SET SESSION innodb_lock_wait_timeout = 5(金融类)或 SET SESSION innodb_lock_wait_timeout = 15(普通 Web)。这样能确保关键路径按需收紧,且避免影响后台批处理任务。
- ORM 框架(如 MyBatis、Hibernate)可能自动复用连接,动态
SET容易被绕过,建议直接配在数据源初始化 SQL 中 - 不要在存储过程中写
SET SESSION—— 多次调用可能覆盖或遗漏 -
innodb_rollback_on_timeout = ON必须开启,否则超时后事务状态不一致,后续操作可能出错
批量更新必须单独设高值,但要加硬约束
报表导出、日终跑批这类任务确实需要放宽等待时间,但绝不能全局设成 300 秒。正确做法是:在执行前临时提升当前会话值,并配合事务拆分。
- 先执行
SET SESSION innodb_lock_wait_timeout = 600 - UPDATE 必须带
LIMIT 1000+ 显式COMMIT循环,避免单事务锁行过多 - 若单次循环耗时 > 600 秒,说明逻辑有缺陷——要么索引缺失,要么数据倾斜,得查执行计划
真正卡住你的从来不是 timeout 值,而是这三件事
看到 Lock wait timeout exceeded; try restarting transaction 错误,第一反应不该是改参数,而是立刻查:SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60。90% 的问题根源在这三处:
- 缺失索引导致
UPDATE全表扫描,把行锁升级成表级资源争抢 - 事务里夹着 HTTP 调用、文件读写、
SLEEP()等非数据库操作,锁持有时间完全不可控 - 隔离级别是
REPEATABLE READ且做了范围查询(如WHERE status IN (1,2)),触发间隙锁,锁住本不该锁的行
参数只是刻度盘,指针动了不代表齿轮没卡死——盯紧哪些 SQL 在抢同一行,比调数字重要得多。











