应优先排查未提交的长事务而非调参;通过 select * from information_schema.innodb_trx where trx_state = 'running' and trx_started
这不是 SQL 写得慢,也不是数据库卡了,而是你的事务在等锁,等太久被 MySQL 主动终止了;优先查谁没提交、谁霸着锁不放,而不是调大
innodb_lock_wait_timeout。怎么快速定位霸占行锁的长事务
别急着杀进程或改参数。先看谁开着事务却不干活:
INNODB_TRX是核心入口,重点筛那些“开了很久却没结束”的记录:
- 执行
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'RUNNING' AND trx_started ,盯住 <code>trx_started超过 1 分钟的事务- 检查
trx_query是否为空(说明事务空挂)、或是否在执行非数据库操作(比如SLEEP()、HTTP 调用、日志写入)- 比对
trx_mysql_thread_id和SHOW PROCESSLIST中状态为Sleep且Time很大的线程 ID —— 基本就是它Spring 应用尤其常见:@Transactional 方法里调了 RPC 却没设超时,或 catch 异常后忘了
rollback,导致事务一直开着。如何确认谁在阻塞、谁在等待
单看
INNODB_TRX只能看到“谁没提交”,但不知道“谁被它挡住了”。必须联查锁等待关系:
- MySQL 5.7 及之前:
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直接对应线程 ID- 注意:
blocking_query为空不等于没事——可能刚执行完 UPDATE 就卡住了,还没来得及发下一条语句真正该 KILL 的是
blocking_pid对应的线程,不是报错的那个等待者。为什么不能直接调大 innodb_lock_wait_timeout
它只是把“等多久就放弃”从 50 秒改成 300 秒,不解决“谁在占着不放”。后果很实在:
- 设成 300 秒后错误变少?说明你掩盖了应用层事务控制缺陷,不是优化
- 全局修改(
SET GLOBAL innodb_lock_wait_timeout = 300)只对新连接生效,当前连接不受影响- 会话级临时调低(
SET SESSION innodb_lock_wait_timeout = 5)反而是好办法——能快速复现问题,逼出隐藏的长事务- 误杀主库同步线程(
User字段为system user)或定时任务线程,可能引发主从延迟甚至数据不一致索引缺失也会让行锁升级成表锁,进一步扩大阻塞范围——所以查完事务,顺手跑一遍
EXPLAIN看执行计划,别漏掉这个环节。












