MySQL服务端通过wait_timeout(非交互式连接)和interactive_timeout(交互式连接)分别控制空闲连接断开时间,连接建立时仅依客户端类型择一生效,不叠加或取最小值。

MySQL 8.0 默认不会“自动维持长连接”,它只会在空闲超时后单向断开;所谓“回收机制”实际由客户端连接池控制,不是 MySQL 本身的功能。
wait_timeout 和 interactive_timeout 到底怎么生效?
这两个参数决定 MySQL 服务端何时关闭空闲连接,但它们不相等、也不总同时起作用:
-
wait_timeout作用于非交互式连接(比如 JDBC、Python 的pymysql、Go 的database/sql),绝大多数应用走这条路 -
interactive_timeout只对带CLIENT_INTERACTIVE标志的连接有效(比如你手动敲mysql -u root -p进去的命令行) - 连接建立时,MySQL 根据客户端是否声明交互模式,**只选其一**生效,不是“取最小值”或“叠加”
- 修改后必须重启 MySQL 或用
SET PERSIST(8.0.22+)才持久化,SET GLOBAL重启即失效
为什么改了 wait_timeout 还是 8 小时就断?
常见原因不是参数没生效,而是你没验证对连接类型:
- 用
show processlist;查看当前连接的Command列:如果是Sleep,再看Time列——这个时间是从最后一条语句执行完开始算的,不是从连接创建开始 - Java 应用默认走非交互模式,所以只认
wait_timeout;但如果你用 Spring Boot + HikariCP,默认会加useSSL=false&serverTimezone=UTC等参数,**不会加CLIENT_INTERACTIVE**,所以改interactive_timeout没用 - 有些云数据库(如阿里云 RDS、腾讯云 CDB)会屏蔽
SET GLOBAL,甚至忽略配置文件里的wait_timeout,必须走控制台参数模板修改
HikariCP / Druid / c3p0 怎么配合 MySQL 避免拿失效连接?
连接池才是关键防线。MySQL 断了,连接池得自己发现并剔除,不能等应用报 Connection reset 或 Lost connection to MySQL server during query 才处理:
- HikariCP 推荐配
connection-test-query=SELECT 1(8.0.19+ 可省略)+validation-timeout=3000+idle-timeout=600000(10 分钟,必须 wait_timeout) - Druid 必须设
testWhileIdle=true+timeBetweenEvictionRunsMillis=60000+minEvictableIdleTimeMillis=1800000(30 分钟) - c3p0 要开
testConnectionOnCheckout=true(每次取都校验,性能损)或更轻量的idleConnectionTestPeriod=3000(5 秒测一次空闲连接) - 所有池都建议关掉
autoReconnect=true—— 官方明确不推荐,事务丢失、会话重置、变量清空问题无法规避
TCP 层断连比 MySQL 超时更隐蔽
即使 wait_timeout 设成 24 小时,中间网络设备(NAT、防火墙、SLB)仍可能在 5–15 分钟内掐断 TCP 连接,此时 MySQL 还“以为”连接活着:
- Linux 客户端需开启内核保活:
echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time(空闲 10 分钟发心跳) - JDBC URL 加
socketTimeout=30000&connectTimeout=5000,避免卡死在底层 read() - 云环境别依赖
tcp_keepalive,改用连接池的健康检查 + 重试(如 HikariCP 的connection-init-sql配SELECT 1) - 错误日志里出现
MySQL server has gone away但show processlist里还有该连接?大概率是 TCP 断了,MySQL 还没感知到
真正要稳住长连接,得在三层协同:MySQL 控制服务端清理节奏、连接池负责客户端连接有效性判断、操作系统和网络层确保 TCP 不被静默回收。漏掉任何一层,都会在凌晨三点抛出那个熟悉的异常。


















