连接池泄漏最直接表现是活跃连接数持续上涨、空闲连接长期为0且接近最大连接数;需监控活跃数趋势、获取连接平均等待时间及“pool exhausted”类错误,并开启泄漏检测定位代码行,再结合线程栈与数据库processlist交叉验证,最后排查三类高频泄漏代码模式。

看连接池监控指标,快速确认是否泄漏
连接池泄漏最直接的表现是活跃连接数(active)持续上涨,空闲连接数(idle)长期为0,且接近或等于最大连接数(maxPoolSize)。比如 HikariCP 日志中出现 active=10, idle=0, waiting=42,就说明所有连接都被占用,新请求正在排队等待——这不是流量突增,而是连接没还回来。
重点盯三个指标:
- 活跃连接数是否随时间单向增长(非周期性波动)
- 获取连接的平均等待时间是否持续升高
- 连接池是否频繁触发
connection pool exhausted或wait millis XXX, active=N, maxActive=N类错误
打开连接泄漏检测,让框架主动报位置
HikariCP 和 Druid 都内置泄漏检测能力,关键是要开启并设合理阈值。生产环境建议配置:
-
HikariCP:设置
leakDetectionThreshold=30000(30秒),日志里会直接打出泄漏栈:
WARN - Connection leak detection triggered for connection ... at com.example.service.UserService.query(UserService.java:45) -
Druid:启用
removeAbandonedOnBorrow=true+removeAbandonedTimeoutMillis=60000,并在日志中搜索abandoned关键词
这个步骤能直接把问题定位到某一行代码,比盲查快十倍。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
抓线程栈和数据库侧连接状态交叉验证
仅靠应用日志还不够,要结合系统层证据确认“连接真没还”:
- 执行
jstack <pid> | grep -A 10 -B 5 "java.sql",看 RUNNABLE 线程是否大量卡在executeQuery、socketRead0等 JDBC 调用上——说明连接借出后卡住了,没走到 close - 登录 MySQL,查
show processlist,重点关注Command=Sleep且Time超过 30 秒的连接,对比其Host和应用服务器 IP 是否一致;再查Threads_connected是否远高于Threads_running - 如果数据库连接数高但活跃查询极少,基本可断定是连接泄漏而非慢 SQL
检查三类高频泄漏代码模式
90% 的泄漏集中在以下写法,逐项扫一遍代码库就能覆盖大部分问题:
-
手动获取连接但没用 try-with-resources 或 finally:
✘Connection conn = ds.getConnection(); stmt.execute(); // 忘了 conn.close()
✔ 改成try(Connection conn = ds.getConnection()) { ... } -
事务内调外部 HTTP/ RPC,导致事务不提交、连接不释放:
比如 @Transactional 方法里调了 FeignClient,下游超时或异常,整个事务卡住,连接一直被持有 -
ResultSet / PreparedStatement 没关闭,间接导致 Connection 归还不生效:
某些老版本驱动或特殊配置下,未关闭 Statement 可能阻塞连接归还逻辑

















