最直接判断mysql连接打满的方法是对比threads_connected与max_connections值是否接近;若前者持续接近后者且threads_running很小,则大概率存在连接泄漏,需用show processlist定位长期sleep或卡住的连接,并检查应用是否正确释放连接、连接池配置与wait_timeout是否匹配。

看 Threads_connected 和 max_connections 是否接近
这是最直接的判断依据。登录 MySQL 执行:SHOW STATUS LIKE 'Threads_connected'; 和 SHOW VARIABLES LIKE 'max_connections';。如果前者数值持续等于或非常接近后者(比如 498/500),就说明连接数已实质打满。此时别急着调大 max_connections,先确认这些连接是真在干活,还是“占着茅坑不拉屎”。
再顺手查下 SHOW STATUS LIKE 'Threads_running';。如果这个值很小(比如只有个位数),而 Threads_connected 却逼近上限,基本可以断定:大量连接处于 Sleep 状态且长期未释放——这是连接泄漏的典型信号。
用 SHOW PROCESSLIST 找出卡住的连接
SHOW PROCESSLIST; 是排查连接池爆满最核心的命令。重点关注三列:
-
Command列为Sleep,且Time值持续增长(几百秒甚至几小时)→ 极大概率是应用拿了连接没归还 -
State列卡在sending data、Locked、Copying to tmp table→ 可能是慢 SQL 或锁等待拖住了连接 -
User和Host能帮你定位是哪个服务实例在“肇事”,尤其在多 Pod、多节点部署时很关键
注意:不要只扫一眼就关掉。建议配合时间点反复执行几次,观察 Id 是否稳定、Time 是否递增。固定不变的 Sleep 连接 ID,就是泄漏嫌疑最大的那几个。
检查应用侧连接是否被正确释放
连接池爆满的根因,80%以上来自应用代码里漏关连接。常见场景包括:
- 异常分支没走到
close(),比如try里抛了异常,finally没写或写错了 - 用了 ORM(如 MyBatis)但事务配置不当,
@Transactional方法内嵌套调用另一个事务方法,导致连接被外层事务长期持有 - 事务里发了 HTTP 请求或调用外部服务,等十几秒才回来,这期间连接一直被占着
- 没用
try-with-resources,也没在finally块中显式关闭Connection/Statement/ResultSet
如果你用的是 Druid,打开 remove-abandoned-timeout 和 log-abandoned-connections;HikariCP 则要确保 leakDetectionThreshold 设了合理值(比如 60000ms),它会在连接疑似泄漏时打日志并输出堆栈。
核对 wait_timeout 与连接池 idleTimeout 是否匹配
MySQL 的 wait_timeout(默认 28800 秒)和连接池的空闲驱逐机制必须协同。否则会出现“连接池以为连接还健康,MySQL 却已经悄悄断开了”的错配。
执行 SHOW VARIABLES LIKE 'wait_timeout';,然后检查你用的连接池配置:
- HikariCP:
idleTimeout必须 小于wait_timeout,建议设为300000(5 分钟) - Druid:
minEvictableIdleTimeMillis同理,也要比wait_timeout小 - 同时建议把 MySQL 的
wait_timeout和interactive_timeout都设为300或600(5~10 分钟),避免空闲连接赖着不走
改完记得执行 SET GLOBAL wait_timeout = 300;(临时生效),或者写进 my.cnf 并重启 MySQL(永久生效)。但注意:K8s 环境下,Pod 重启前务必加 preStopHook,留出几秒让连接优雅释放,否则会残留一堆半开连接。
真正难的不是查出哪条连接卡住了,而是确认它为什么卡住——是代码逻辑缺陷、事务设计问题,还是下游依赖拖慢了整个事务生命周期。这类问题往往藏在异常路径或边缘 case 里,日志不打全、监控没覆盖,就只能靠反复抓堆栈和复现时间点来交叉验证。











