必须同步调整连接池maxLifetime并验证事务是否及时提交,否则Sleep进程照常堆积甚至引发MySQL server has gone away;因连接池未主动淘汰老化连接、配置未重启生效、事务未提交导致假Sleep,或JDBC autoReconnect掩盖断连。

只改 wait_timeout 和 interactive_timeout 不够,必须同步调整连接池的 maxLifetime 并验证事务是否及时提交,否则 Sleep 进程照常堆积,甚至引发 MySQL server has gone away。
为什么调了超时参数却还有大量 Sleep 连接?
常见错觉是“改完配置就万事大吉”,实际失效原因很具体:
- 应用用的是连接池(如 HikariCP、Druid),连接被复用后长期未归还,但池子没做主动淘汰——MySQL 虽已 kill 掉该连接,池子仍把它当“可用”返回给业务
- 配置只写了
my.cnf却没重启 MySQL,或用了SET GLOBAL但权限不足/未持久化,导致SHOW VARIABLES看着对,实际运行时仍是默认值 - 连接卡在事务里:没
COMMIT或ROLLBACK,状态是Sleep但Time值不涨(因为不算“空闲”),wait_timeout完全不触发 - JDBC URL 里开了
autoReconnect=true,掩盖了底层 TCP 断连,MySQL 不知情,连接池也误判为活跃
必须同时设置的三个 MySQL 参数
这三个值要一致、可推演、留缓冲:
-
wait_timeout = 300:非交互式连接(Java/JDBC/PHP PDO 等)空闲 5 分钟断开;生产建议 300~600,别设低于 180,否则重连风暴压垮服务端 -
interactive_timeout = 300:命令行、Navicat、DBeaver 等工具连接也按同样策略清理;必须和wait_timeout同值,否则客户端行为不可控 -
connect_timeout = 10:TCP 握手+认证阶段最长等 10 秒,防慢网或扫描器占满max_connections;设成 60 是典型反模式
注意:net_read_timeout 和 net_write_timeout 控制查询执行中卡顿容忍度,跟 Sleep 堆积无关,先不动。
HikariCP / Druid 必须对齐 maxLifetime
MySQL 断连接只是第一步,连接池必须“提前退休”已老化连接:
- HikariCP:
maxLifetime=294000(4.9 分钟),比wait_timeout=300少 60 秒,留出网络延迟与服务端处理时间差 - Druid:
maxEvictableIdleTimeMillis=294000,逻辑同上;别依赖testOnBorrow,它每次取连接都跑SELECT 1,QPS 高时直接拖垮 DB - 推荐用
connectionInitSql=SELECT 1+keepaliveTime(HikariCP 5.0+)做轻量心跳,不增加业务线程负担
怎么确认配置真生效了?
别信配置文件,看运行时真实行为:
- 执行
SHOW PROCESSLIST;,重点找State = 'Sleep'且Time > 300的行——如果还有,说明参数未生效,或该连接正处在未结束的事务中 - 查
information_schema.INNODB_TRX,对比TRX_STARTED时间戳和PROCESSLIST中的Time,能快速区分“真空闲”和“假 Sleep” - 临时改参用
SET GLOBAL wait_timeout = 300后,新建立的连接才生效;已有连接不受影响,需等自然淘汰或重启应用连接池
最易被忽略的一点:Spring @Transactional 方法里调 HTTP 请求或文件读写,事务实际持锁时间远超 SQL 执行本身,MySQL 只认 BEGIN 到 COMMIT 的整段时间——这种“隐式长事务”才是 Sleep 堆积的根因,超时参数只是兜底手段。


















