锁等待超时错误长什么样
执行
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 = <blocking_thread>;特别留意状态为
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为什么没走索引。
