这不是数据库卡了,而是事务等锁超时被MySQL主动终止;应优先查未提交事务,通过INNODB_TRX定位长时间运行且trx_query为空或执行耗时操作的事务,并联查INNODB_LOCK_WAITS确认阻塞源。

直接结论:这不是数据库卡了,是你的事务在等锁,等太久被 MySQL 主动扔掉了;优先查谁没提交,而不是调大 innodb_lock_wait_timeout。
怎么快速定位正在霸占锁的未提交事务
别先看慢日志或怀疑 SQL 本身——Lock wait timeout exceeded 的 SQL 往往执行飞快,只是前面卡在等锁。关键看 INNODB_TRX 里那些“开了很久却没结束”的事务:
- 运行
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'RUNNING' AND trx_started < DATE_SUB(NOW(), INTERVAL 60 SECOND);,重点盯trx_started时间超过 1 分钟的记录 - 检查
trx_query是否为空(说明事务空挂着),或是否在执行耗时操作(比如调外部 HTTP、发邮件、SLEEP()) - 比对
trx_mysql_thread_id和SHOW PROCESSLIST中状态为Sleep且Time很大的线程 ID,基本就是它 - Spring 应用尤其注意:@Transactional 方法里调了 RPC 却没设超时,或 catch 异常后忘了 rollback
为什么杀错线程反而更糟
盲目 KILL 看起来快,但容易误杀正在正常工作的事务,甚至引发数据不一致。真正该杀的是阻塞源,不是等待者:
- 先联查锁等待关系:
SELECT r.trx_id waiting_trx_id, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_query blocking_query FROM INFORMATION_SCHEMA.INNODB_TRX r INNER JOIN INFORMATION_SCHEMA.INNODB_LOCK_WAITS w ON r.trx_id = w.requesting_trx_id INNER JOIN INFORMATION_SCHEMA.INNODB_TRX b ON b.trx_id = w.blocking_trx_id; - MySQL 8.0 改用
sys.innodb_lock_waits视图,字段名变成waiting_pid/blocking_pid - 确认
blocking_query是调试残留、死循环还是业务异常卡住,再决定是否KILL - 别用
KILL干掉主库同步线程(User字段为system user)或关键定时任务
调整 innodb_lock_wait_timeout 的真实作用
它只是改“等多久就放弃”,不解决“谁在占着不放”。设成 300 秒,只是让问题延迟暴露,不是修复:
- 会话级临时调低(如
SET SESSION innodb_lock_wait_timeout = 5;)可用于快速复现问题,逼出隐藏长事务 - 全局修改需重启或
SET GLOBAL(但新连接才生效),生产环境慎用 - 如果调大后错误变少,说明你掩盖了应用层事务控制缺陷,不是优化
- 索引缺失导致锁升级(比如
UPDATE ... WHERE name='xxx'无索引)时,调 timeout 毫无意义——得加索引
最容易被忽略的两个点
一是 autocommit=0 被遗忘:开发连上 MySQL 后手动 START TRANSACTION,改完一行就走人,连接还连着,锁一直挂着;二是 SELECT ... FOR UPDATE 范围过大,比如没带 WHERE 条件或条件没走索引,结果锁了一整张表——这种场景下,innodb_lock_wait_timeout 再小也救不了。


















