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 = Sleep 且 Time 很大的连接,基本可以判断连接拿出去后没有及时归还,也就是典型的连接泄漏。

从应用侧定位泄漏源头

数据库只负责展示连接状态,真正要修的是应用代码。
先在应用服务器上用 ss 或 netstat 看当前到 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
系统公告

泽御云中秋国庆双节活动上线:新购8折,拼团3.99元起

尊敬的用户:
泽御云“月满中秋·礼贺国庆”双节活动现已开启,活动时间为2026年9月23日至10月10日。 活动期间可享以下福利:
1. 常规云服务器新购使用优惠码“泽御中秋国庆同乐”,符合条件的订单享8折优惠。
2. 香港精品云服务器5人拼团低至3.99元,部分4核4G套餐3人拼团年付388元,续费同价。
3. 新用户购买年付云服务器,符合活动规则可赠送2个月使用时长。
4. 老用户续费季度赠15天,续费年度赠2个月;活动期间升级配置免收配置迁移手续费。
5. 推荐好友成功下单,符合条件的推荐人可获赠7天服务器使用时长。
6. 活动期间享宕机补偿标准翻倍、简单网站迁移协助及技术工单优先处理权益。
温馨提示:优惠码不适用于拼团套餐、活动轻量产品、年付订单及续费订单;拼团套餐为独立特价活动,不与赠时类福利叠加。赠送时长不可折现、退款或跨账户转移,具体规则以活动页面说明为准。
服务中心
客服
在线客服
24小时为您服务
咨询
联系我们
联系我们,为您的业务提供专属服务。
24/7 技术支持
如果您遇到寻求进一步的帮助,请过工单与我们进行联系。
24/7 即时支持
泽御云
售前客服
泽御云
泽御云
售后客服
泽御云
技术支持
评价
您对当前页面的整体感受是否满意?
😞
非常不满意
😕
不满意
😐
一般
🙂
满意
😊
非常满意