锁等待超时错误表现为“ERROR 1205 (40001): Lock wait timeout exceeded”,即事务等待锁超时(默认50秒),非死锁;可通过INNODB_TRX等系统视图定位阻塞源,重点排查Sleep状态但未提交事务的连接;避免方法包括:走索引、缩短事务、拆分批量操作、降隔离级别、慎用FOR UPDATE,而非依赖调高超时参数。

锁等待超时错误长什么样
执行 INSERT、UPDATE 或 DELETE 时突然报错:
ERROR 1205 (40001): Deadlock found when trying to get lock; try restarting transaction或更常见的是:
ERROR 1205 (40001): Lock wait timeout exceeded; try restarting transaction这说明事务在等某个锁,但等了太久(默认 50 秒),MySQL 主动放弃了。不是死锁,是“干等超时”。
查谁在占着锁不放
先连上 MySQL,用系统视图定位阻塞源头:
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query FROM information_schema.INNODB_TRX r INNER JOIN information_schema.INNODB_LOCK_WAITS w ON w.requesting_trx_id = r.trx_id INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id;
如果返回空,说明锁已释放或没发生等待;有结果就看 blocking_query 是什么语句,再查对应线程:
SELECT * FROM information_schema.PROCESSLIST WHERE ID =特别留意状态为;
Sleep 却持有事务未提交的连接——这是最常见的“幽灵锁源”。
怎么避免锁等太久
根本思路是缩短锁持有时间 + 减少锁冲突概率:
- 所有写操作尽量走索引,避免全表扫描导致锁住大量无关行;检查
EXPLAIN输出,确认type不是ALL - 事务里只做必要操作,不要在事务中调外部 API、发邮件、等用户输入
- 批量更新改用
IN拆小批次(比如每次 100 行),别一次性UPDATE ... WHERE id IN (1..10000) - 读已提交(
READ COMMITTED)比可重复读(REPEATABLE READ)锁范围更小,尤其对非唯一条件查询;可通过SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;临时调整 - 显式加锁慎用
SELECT ... FOR UPDATE,优先考虑应用层幂等+乐观锁(如UPDATE ... SET version=version+1 WHERE id=? AND version=?)
超时参数能调但不能依赖
innodb_lock_wait_timeout 默认 50 秒,可在会话级临时调大:
SET SESSION innodb_lock_wait_timeout = 120;但这是掩耳盗铃。真正该调的是
wait_timeout 和 interactive_timeout(控制空闲连接自动断开时间),防止应用不关连接导致事务长期挂起。生产环境不建议全局调高 innodb_lock_wait_timeout,它掩盖的是设计缺陷,不是性能瓶颈。
并发异常处理的关键不在兜底重试逻辑多漂亮,而在于让锁不出现——或者出现后 1 秒内就释放。很多团队花半天写重试装饰器,却没查过慢查询日志里那条跑了 8 秒的 UPDATE 为什么没走索引。










