先确认是否sleep连接导致:若threads_connected接近max_connections而threads_running极低,再查processlist中state='sleep'且time>300的连接,多因wait_timeout与连接池空闲回收时间不匹配所致。

怎么看是不是Sleep连接在捣鬼
别一看到 Too many connections 就去改 max_connections。先连上 MySQL(用 root 本地 socket,它通常还能进),执行:
SHOW STATUS LIKE 'Threads_connected';<br>SHOW STATUS LIKE 'Threads_running';<br>SHOW PROCESSLIST;
如果 Threads_connected 接近 max_connections,但 Threads_running 很小(比如 500 连里只有 3 个在跑),那基本就是一堆 Sleep 连接卡着不走。再重点看 PROCESSLIST 里的 State 和 Time 列:State = 'Sleep' 且 Time > 300 的连接,90% 是泄漏或空闲回收没跟上。
为什么Sleep连接死活不释放
根本原因通常是两端超时设置打架:wait_timeout 和连接池的空闲回收时间不匹配。MySQL 默认 wait_timeout = 28800(8 小时),而应用侧如果没调,连接池会一直认为连接还活着。
-
wait_timeout和interactive_timeout必须同时设,且值一致(建议 300~600 秒) - HikariCP 的
idle-timeout必须严格小于wait_timeout,差值最好 ≤ 60 秒 - Druid 的
min-evictable-idle-time-millis+time-between-eviction-runs-millis组合结果,也得比wait_timeout小 - 改完要确认生效:动态执行
SET GLOBAL wait_timeout = 300;后,新连接才按这个算;老的Sleep连接仍按旧值倒计时
怎么快速定位泄漏源头
靠人眼扫 PROCESSLIST 效率低,优先查三处:
— 查账号和来源:SELECT USER, HOST, COUNT(*) FROM information_schema.PROCESSLIST GROUP BY USER, HOST; 看是不是某个定时脚本、监控工具或某台应用服务器 IP 突然连了上百个
— 查连接池监控:HikariCP 开 leak-detection-threshold=60000,Druid 开 removeAbandonedOnBorrow=true 且 removeAbandonedTimeout=300,泄漏发生时直接打堆栈
— 查代码里没关连接的地方:尤其注意异常分支、try 块末尾写 close()(不会执行)、@Transactional 里调外部 HTTP 导致事务长期挂起
临时清理和长期防复发的关键点
KILL 大量 Sleep 连接只是抢出几个名额,几小时后还会回来。真正要盯住的是协同关系:
- 连接池最大数 × 实例数,不能超过
max_connections的 70%,留出 buffer 给运维和脚本 - 每个连接平均吃内存 256KB~1MB,设太高可能触发 OOM,不是越大越安全
-
ulimit -n和 systemd 的LimitNOFILE必须同步调大,否则max_connections设再高也启动失败 - 最易被忽略的是:
wait_timeout改了,但interactive_timeout没动,导致某些 ORM 初始化连接或运维脚本连上来就挂半天
问题从来不在单点参数,而在 MySQL、连接池、业务代码三者之间那几毫秒的错位。一次 Sleep 连接堆积,往往暴露的是三个地方的配置都没对齐。











