Lock wait timeout exceeded 不一定是锁本身的问题,主因是事务持锁时间过长,如慢SQL、非DB操作混入事务、缺失索引导致全表扫描等,需从SQL优化、事务拆分、隔离级别调整及热点行解耦入手解决。

为什么 Lock wait timeout exceeded 不一定是锁本身的问题
这个错误常被误认为是“锁太重”或“事务没提交”,但实际多数情况源于事务持有锁的时间远超预期。比如一个本该毫秒级完成的更新,因前端重试、应用层循环调用、或未加索引导致全表扫描,最终让 UPDATE 卡住几秒甚至几十秒——其他并发事务就在等它释放行锁,等不及就报超时。
- 先查当前阻塞链:
SELECT * FROM performance_schema.data_lock_waits;(MySQL 8.0+)或SHOW ENGINE INNODB STATUS\G中的TRANSACTIONS部分 - 重点看
WAITING FOR THIS LOCK TO BE GRANTED和HOLDS THE LOCK(S)对应的事务 ID 和 SQL - 检查被等待的那条语句是否走了索引——
EXPLAIN看type是否为ALL或index
如何缩短行锁持有时间:从 SQL 到事务边界
锁等待时间 = 事务开始到提交之间所有操作耗时。哪怕只有一行更新,如果事务里混了 HTTP 调用、日志写入、循环计算,锁就一直挂着。
- 把非数据库操作(如发消息、调第三方 API)全部移出事务块,用最终一致性替代强一致
- 避免在事务中做
SELECT ... FOR UPDATE后长时间处理再更新;改成先查、再算、最后UPDATE ... WHERE id = ? AND version = ?做乐观锁 - 批量更新拆成小事务:比如每 100 行
COMMIT一次,而不是一次性更新 10 万行 - 确认隔离级别:如果不是强一致性必需,把
REPEATABLE READ降为READ COMMITTED,能减少间隙锁范围
innodb_lock_wait_timeout 能调多大?别乱设
默认 50 秒看似很长,但调到 300 甚至 3600 只会让问题更隐蔽——用户请求卡住五分钟才失败,体验反而更差。这个参数本质是“兜底熔断”,不是性能优化手段。
- 线上建议保持默认或略减(如设为 30),配合监控告警快速发现长事务
- 绝对不要在会话级临时加大它来“解决”超时,这只会掩盖慢 SQL 和事务设计缺陷
- 真正要调的是
innodb_rollback_on_timeout:MySQL 5.7+ 默认OFF,意味着超时后事务不自动回滚,锁仍挂着——务必设为ON,否则一个超时会引发连锁等待
并发写入热点行怎么破:避开争抢才是关键
比如订单表按 user_id 分片后,某个 VIP 用户高频下单,所有写请求还是打到同一行(如用户余额记录),再好的索引也扛不住。
- 用“记账流水表 + 异步汇总”代替直接更新余额字段:每次下单插入一条
account_log记录,余额由定时任务或触发器聚合 - 对计数类字段(如点赞数),改用 Redis + 定时落库,MySQL 只存最终值
- 必要时引入应用层分段锁:比如把一个用户余额拆成 4 个子账户(
balance_0~balance_3),写入时随机选一个更新,读时 sum
锁等待超时从来不是孤立的数据库配置问题,而是 SQL 效率、事务粒度、业务模型三者耦合的结果。最容易被忽略的是:开发阶段没压测真实并发路径,上线后靠错误日志倒推瓶颈——这时候看到的往往已经是雪球滚大的结果了。











