只调wait_timeout和interactive_timeout不能单独解决僵尸连接问题,必须同步配置连接池maxlifetime(如hikaricp设为比wait_timeout小60秒)、启用连接有效性验证,并规范事务管理,否则仍会因复用已关闭连接而报错。

直接结论:只调 wait_timeout 和 interactive_timeout 就能解决大部分“僵尸连接”问题,但必须配合应用层连接池的 maxLifetime 设置,否则照样断连报错。
为什么 Sleep 连接不自动断开?
MySQL 的 wait_timeout 默认是 28800 秒(8 小时),只要连接状态是 Sleep 且空闲时间超过该值,就会被服务端主动 kill。但很多场景下它没生效,常见原因有:
- 应用用的是连接池,连接被复用后长时间未归还,但池子自己没检测或没设超时,导致连接在池里“假活”
- 配置改了但没生效:只改了
my.cnf却忘了重启 MySQL,或用了SET GLOBAL但权限不足/未持久化 - 事务没提交或回滚,连接一直处于
active状态,wait_timeout完全不触发 - 客户端设置了长连接保活(如 JDBC 的
autoReconnect=true),但底层 TCP 层已断,MySQL 不知情
必须同步设置的三个关键参数
这三个参数要一起调,缺一不可:
-
wait_timeout = 600:非交互式连接(即你应用代码连的)空闲 10 分钟就断。生产环境建议 600~1800,别低于 300,太短会频繁重连 -
interactive_timeout = 600:命令行、Navicat 等交互式工具也按同样策略清理,避免开发连着不关导致堆积 -
connect_timeout = 10:控制 TCP 握手和认证阶段最长等多久,防止慢网或恶意扫描拖住线程;设太高(比如 60)反而容易积压无效连接请求
注意:net_read_timeout 和 net_write_timeout 影响的是查询执行中读写数据的卡顿容忍度,和“僵尸连接”关系不大,先别动。
连接池端必须对齐 maxLifetime
光靠 MySQL 断连接不行——应用拿到一个已被服务端关闭的连接,下次用时直接报 MySQL server has gone away。所以连接池必须主动“提前退休”:
- HikariCP:设
maxLifetime为 540000(9 分钟),比wait_timeout=600少 60 秒,留出缓冲 - Druid:对应参数是
maxEvictableIdleTimeMillis,同样建议设为比wait_timeout * 1000 - 60000 - 别依赖
testOnBorrow或validationQuery:它们每次取连接都查一次库,开销大;优先用keepaliveTime+ 心跳(如 HikariCP 的connectionInitSql=SELECT 1)
验证是否真生效了
别只信配置文件或 SHOW VARIABLES,要看运行时行为:
- 跑
SHOW PROCESSLIST;,找State = 'Sleep'且Time > 600的行 —— 如果还有,说明参数没生效或连接正处在事务中 - 查
SHOW VARIABLES LIKE '%timeout%';,确认wait_timeout和interactive_timeout真是你要的值(不是默认 28800) - 用
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60;排查长事务,这类连接不会受wait_timeout管控
最常被忽略的一点:wait_timeout 只管空闲连接,不管事务内挂起的连接。如果应用开了事务却忘了 commit,哪怕设成 10 秒也没用 —— 那得靠代码兜底或 innodb_lock_wait_timeout 配合监控来发现。











