MySQL连接泄漏,应用长连接过多耗尽连接池排查

MySQL 连接池被耗尽,最常见的表现是应用报 Too many connections 或接口突然大面积超时。
这个问题多半不是单条 SQL 慢,而是应用侧把连接借出去之后没有正确归还,时间一长,连接数只涨不降。
本文会从查连接数、定位源头、调整参数到验证恢复,带你按顺序把问题找出来并处理掉。

先确认连接数真的满了

登录 MySQL 后,先看当前连接总数和上限:

SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';

如果 Threads_connected 长时间接近 max_connections,说明连接池确实被占满。
再用下面这条 SQL,按用户和主机维度看谁占用的连接最多:

SELECT user, host, db, COUNT(*) AS cnt
FROM information_schema.processlist
GROUP BY user, host, db
ORDER BY cnt DESC;

也可以直接看 processlist 里每个连接的 Command 和 State,快速判断是空闲连接还是正在跑查询:

SELECT id, user, host, db, command, time, state, info
FROM information_schema.processlist
ORDER BY time DESC;

如果看到大量 Command = SleepTime 很大的连接,基本可以判断连接拿出去后没有及时归还,也就是典型的连接泄漏。

从应用侧定位泄漏源头

数据库只负责展示连接状态,真正要修的是应用代码。
先在应用服务器上用 ssnetstat 看当前到 MySQL 3306 端口的连接数:

ss -ant | grep ':3306' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c

如果某个应用实例连接数特别高,就重点查这个实例的日志和连接池监控。
常见原因有三类:

  • 使用 DataSource 时没走 try-with-resources,异常路径下 connection.close() 没执行。
  • 事务操作中手动创建了 Connection,但事务提交或回滚后忘记释放。
  • 连接池的最大连接数设置过大,超过了 MySQL 的 max_connections,比如 MySQL 上限 500,连接池却配了 600。

如果是 Java 应用,可以开启连接池的 leakDetectionThreshold(HikariCP)或 removeAbandonedTimeout(Druid),让连接池自动检测并回收超时未归还的连接。
下面是一个 HikariCP 的配置片段:

spring:
  datasource:
    hikari:
      maximum-pool-size: 50
      minimum-idle: 10
      connection-timeout: 30000
      idle-timeout: 600000
      leak-detection-threshold: 60000

leak-detection-threshold: 60000 表示连接借出超过 60 秒未归还就打印告警日志,但不会强制回收,主要用于发现问题。
Druid 则可以用 removeAbandoned=true 强制回收。

调低连接池上限并设置连接生命周期

连接池不是越大越好。
如果并发不高,把 maximum-pool-size 调小反而能保护数据库。
同时加上连接最大存活时间和空闲回收,避免长连接长期占用:

spring:
  datasource:
    hikari:
      maximum-pool-size: 30
      minimum-idle: 5
      max-lifetime: 1800000
      idle-timeout: 600000

max-lifetime 建议小于 MySQL 的 wait_timeout
可以在 MySQL 里查看:

SHOW VARIABLES LIKE 'wait_timeout';

如果 wait_timeout 是 28800(8 小时),
应用侧 max-lifetime 建议设为 1800000(30 分钟),
这样即使有连接没关闭,
MySQL 也会在超时后主动断掉一部分空闲连接。

如果应用和 MySQL 之间有防火墙或代理(比如 ProxySQL、
云数据库代理),
还要检查代理层的空闲超时,
有时连接是被代理切断了,
但应用不知道,
继续使用就会报错,
这类错误会被误认为是连接泄漏。

处理已经堆积的连接

在应用修复并重新发布前,可以先临时清理空闲连接,让服务恢复可用。
MySQL 中不能直接 kill 所有 Sleep 连接,但可以写一条拼接 SQL:

SELECT CONCAT('KILL ', id, ';')
FROM information_schema.processlist
WHERE command = 'Sleep' AND time > 300;

把查询结果复制出来执行,或者用存储过程循环执行。
注意这只是一个应急手段,如果代码不修,新连接很快又会堆积。

更好的做法是重启应用,让连接池重新建立连接。
如果应用无法立即重启,只能靠调低 wait_timeout 让 MySQL 更快回收空闲连接,例如临时设置为 300 秒:

SET GLOBAL wait_timeout = 300;

这个设置会在 MySQL 重启后失效,不能当作长期方案。

验证问题是否解决

按照前面的步骤操作后,重点观察以下几点:

  • SHOW STATUS LIKE 'Threads_connected'; 查看连接数是否回落到正常水位,且不再持续上涨。
  • SHOW PROCESSLIST; 观察 Sleep 连接是否维持在低位,没有大量长时间空闲连接。
  • 查看应用日志,确认没有新的 Too many connections 或连接获取超时错误。

如果你给连接池加了泄漏检测,日志中也不再出现 Connection leak detection 相关警告,说明泄漏点已经处理掉。

还有一点很容易忽略:连接池的选择和参数配置要匹配实际框架版本。
Druid、HikariCP、dbcp 的默认参数不同,调整前先确认你用的到底是哪种连接池,再查对应版本的官方文档,不要照搬别人的配置。
如果你用的云数据库或自建 MySQL 版本不同,information_schema.processlist 的字段基本一致,但个别版本可能没有 db 字段,以实际输出为准。

排查 MySQL 连接泄漏的核心思路就是三个字:看、杀、修。
先看连接哪里来,再杀长时间空闲连接应急,最后在应用代码或连接池配置里把归还逻辑修好。
按照本文的步骤操作,大多数连接池耗尽问题都能在半小时内定位到方向,剩下的就是改代码和发布验证了。

分享到:
上一篇
Ubuntu24.04关闭不必要systemd服务
下一篇
Docker容器网络模式选型bridge/host/
1
系统公告

机房迁移升级通知

尊敬的用户: IP 段 103.23.148.x、156.224.29.x 原香港一区线路波动、攻击频繁,平台定于 7 月 5 日凌晨分批迁移至香港 GIA 机房,硬件升级 AMD 铂金机型。 迁移均在凌晨操作,最大程度降低业务影响,迁移期间服务器临时关机; 升级后配置不降低、费用不涨价,数据默认同步迁移; 迁移后 IP 全部更换,请及时修改域名解析、防火墙白名单; 建议提前备份重要数据,有问题可联系在线客服。 感谢理解与支持! 泽御云科技 2026.06.30
服务中心
客服
在线客服
24小时为您服务
咨询
联系我们
联系我们,为您的业务提供专属服务。
24/7 技术支持
如果您遇到寻求进一步的帮助,请过工单与我们进行联系。
24/7 即时支持
泽御云
售前客服
泽御云
泽御云
售后客服
泽御云
技术支持
评价
您对当前页面的整体感受是否满意?
😞
非常不满意
😕
不满意
😐
一般
🙂
满意
😊
非常满意