mysql空闲连接超时由wait_timeout参数控制,它决定非交互式连接(如jdbc)空闲多久后被服务端主动断开;interactive_timeout仅影响交互式连接(如mysql命令行),但业务连接通常遵循wait_timeout。

MySQL 的 wait_timeout 必须显式调小,否则连接池里大量“活着但已失效”的连接会持续堆积,最终引发 Communications link failure 或 Lost connection 错误——这不是网络问题,是服务端主动断连后客户端没感知。
为什么改了 wait_timeout 还是报 “MySQL server has gone away”?
常见错误现象:应用日志频繁出现 Communications link failure,但数据库本身没重启、也没明显负载 spike;show processlist 里能看到一堆 Sleep 状态且 Time 值远超你设的 wait_timeout。
根本原因不是 MySQL 没生效,而是连接池的空闲驱逐策略和 MySQL 不匹配:
- 连接池(如 HikariCP)的
idleTimeout默认是 10 分钟,而 MySQL 的wait_timeout是 8 小时(28800 秒) - 连接池以为连接还健康,MySQL 却在后台悄悄关掉了它
- 下次从池子里取出这个连接执行 SQL,才第一次暴露问题
解决办法:让连接池比 MySQL 更早“动手”。例如 MySQL 设为 1800 秒(30 分钟),连接池 idleTimeout 就设成 1700 秒(28 分 20 秒)。
怎么安全地修改 wait_timeout 和 interactive_timeout?
这两个参数必须一起设,否则会出现会话行为不一致。JDBC 连接属于非交互式,只读 wait_timeout;但命令行 mysql 工具用的是 interactive_timeout。如果只改一个,运维排查时容易误判。
推荐操作:
- 先查当前值:
show global variables like '%timeout%'; - 临时生效(重启失效):
set global wait_timeout = 1800;和set global interactive_timeout = 1800; - 永久生效:编辑
/etc/my.cnf,在[mysqld]下添加两行:wait_timeout = 1800<br>interactive_timeout = 1800
- 改完必须重启 MySQL:
systemctl restart mysqld(或docker restart mysql)
注意:Linux 下最大允许值是 31536000(1 年),Windows 是 2147483;设成 0 无效,最小只能是 1。
为什么不能只靠 JDBC 的 socketTimeout 挡住空闲断连?
socketTimeout 控制的是单次读/写操作的等待上限(比如执行一条 SELECT 卡住 30 秒就抛异常),它对“连接空闲”完全无感。MySQL 断掉一个空闲连接时,TCP 连接可能还处于 ESTABLISHED 状态,socketTimeout 根本不会触发。
真正起作用的是 TCP keepalive 和 MySQL 自身的空闲检测逻辑。所以:
-
socketTimeout解决的是“查询卡死”,不是“连接僵死” -
connectTimeout解决的是“连不上”,不是“连上了但过期” - 只有
wait_timeout+ 连接池idleTimeout配合,才能覆盖完整生命周期
如果你用的是 HikariCP,务必检查 spring.datasource.hikari.idle-timeout 是否小于 wait_timeout;Druid 则对应 minEvictableIdleTimeMillis。
改完之后怎么验证是否生效?
别只信配置文件或 show variables,要实测连接行为:
- 用 JDBC 连上,执行
select sleep(3100);(超 30 分钟),看是否真被 kill - 查
show processlist,观察 Sleep 连接的Time列是否在接近你设的秒数时消失 - 在应用侧模拟低频请求:启动后等 35 分钟再发第一条 SQL,确认是否重建连接而非复用旧连接
最容易被忽略的一点:Docker 容器里改了 my.cnf 但没挂载进容器,或者挂载了却忘了 restart,导致配置始终没加载。直接进容器执行 ps aux | grep mysql,看启动命令是否带 --defaults-file,再确认文件路径是否真实存在。











