wait_timeout对僵尸事务无效,因其仅作用于sleep状态的空闲连接,而网络抖动导致的卡住事务线程处于updating/locked等活跃状态,不触发超时机制。

MySQL 本身不会自动回滚因网络抖动中断但未提交的事务——它只会在连接真正断开且超时后关闭连接,而事务状态仍维持在 Running 或 Locked,直到被手动 KILL 或等待锁超时。靠调 wait_timeout 只能清理空闲连接,对“卡住的事务”无效。
为什么 wait_timeout 对僵尸事务没用
wait_timeout 只作用于 Sleep 状态的连接,即事务已结束、语句执行完、正空闲等待下一条命令的状态。而网络抖动后,如果事务中某条 UPDATE 或 SELECT ... FOR UPDATE 已发出但客户端收不到响应,MySQL 线程仍处于 Updating / Locked / Writing to net 等活跃状态,wait_timeout 完全不触发。
- 查
SHOW PROCESSLIST时,这类线程的State不是Sleep,而是带锁或写入中的状态 -
Time字段可能很小(比如 -
wait_timeout和interactive_timeout对这种场景完全无感知
如何识别并处理网络抖动残留的僵尸事务
重点不是等超时,而是主动发现 + 快速干预。真实运维中需组合判断:
- 用
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 300找出运行超 5 分钟的事务(注意:trx_started是事务开始时间,不是连接创建时间) - 关联
information_schema.PROCESSLIST查ID和INFO,确认是否INFO IS NULL(大概率已失联)且State异常(如Waiting for table metadata lock卡死) - 避免直接
KILL所有长事务——先SELECT * FROM performance_schema.threads WHERE PROCESSLIST_ID = ?确认线程没在做备份或 DDL - 批量
KILL时加LIMIT 5控制节奏,防止innodb_thread_concurrency被打满
应用层必须配合的三项实操动作
单靠数据库配置无法根治,网络抖动是常态,关键在应用怎么扛:
- 所有 JDBC 连接 URL 加上
socketTimeout=30000(单位毫秒),让驱动在读响应超时时主动抛异常,而不是无限等下去 - Spring Boot + MyBatis 场景下,确保
@Transactional方法内不出现未捕获的RuntimeException,否则事务挂起、连接不归还 - HikariCP 必须设
max-lifetime=3500(秒),同时 MySQLwait_timeout=3600,留出 100 秒缓冲——这样连接池回收时,MySQL 还没来得及 kill 它
别踩的坑:自动 KILL 脚本 + wait_timeout 双误杀
写个脚本每分钟扫 PROCESSLIST 并 KILL 所有 Time > 600 的 Sleep 连接,看似干净,实则危险:
- 会误杀正在做慢查询的后台任务线程(
State=Sending data但Time高) - 如果脚本没过滤
USER != 'system user'和COMMAND != 'Sleep',可能干掉复制线程或Event_scheduler -
wait_timeout设太小(如 60 秒)+ 连接池没配keepalive-time,会导致连接频繁重建,QPS 拉高时反而压垮 MySQL
真正的稳定点不在“杀得多快”,而在“连得有多韧”——网络抖动不可控,但连接生命周期和事务边界必须由代码明确定义。











