mysql连接池泄露表现为服务变慢、show processlist出现大量sleep连接、max_connections耗尽但无报错;本质是连接未被真正归还池中,需确保getconnection()必有对应且必定执行的close()归还动作,并开启druid或hikaricp泄漏检测(如leakdetectionthreshold=60000)以捕获调用栈定位源头。

连接没关,但程序没报错怎么办
MySQL 连接池泄露最典型的表象是:服务跑着跑着变慢、SHOW PROCESSLIST 里出现大量 Sleep 状态的连接、max_connections 被耗尽,但应用日志里几乎不报错。这不是“没出问题”,而是连接被申请后没被显式释放,还卡在池子里占着资源。
关键判断点:检查连接是否真被归还给池子,而不是靠 try-catch 或 finally 里调用 close() 就以为万事大吉——很多连接池(比如 HikariCP、Druid)的 close() 实际上只是归还,不是销毁;如果归还逻辑被跳过(比如异常提前 return、线程中断、异步回调未执行),连接就永远滞留。
- 务必确认所有
getConnection()都有对应且**必定执行**的归还动作(如HikariDataSource的Connection.close()) - 避免在 connection 上做长耗时操作(如大文件处理、外部 HTTP 调用),否则连接会卡住,等效于“泄露”
- 不要在 finally 块外写
return,也不要依赖 JVM shutdown hook 清理连接——它不保证触发时机
Druid 和 HikariCP 的泄漏检测开关怎么开
两个主流连接池都内置了泄漏检测,但默认关闭。不开等于盲跑。
Druid 需在数据源配置中加:removeAbandonedOnBorrow=true、removeAbandonedTimeoutMillis=60000(单位毫秒),并开启日志级别为 DEBUG,它会在借连接超时后强制回收并打印堆栈;HikariCP 则靠 leakDetectionThreshold(单位毫秒),设为 60000 表示借出后 60 秒未归还就报警。
- Druid 的
removeAbandonedOnBorrow在高并发下可能误判,建议只在测试/预发环境开启;生产建议用logAbandoned=true+ 定期查日志 - HikariCP 的
leakDetectionThreshold设太小(如 5000)会导致频繁误告警,设太大(如 300000)又起不到及时发现作用,60000 是较稳妥起点 - 两者都会在控制台或日志中输出类似
Connection leak detection triggered的提示,并附带getConnection()调用栈 —— 这就是定位泄漏源头的关键线索
如何用 SHOW PROCESSLIST 快速筛出可疑连接
SHOW PROCESSLIST 是最轻量、最直接的现场快照工具,不用重启、不依赖应用日志。
重点关注 Command 列为 Sleep、Time 值持续增长(比如 > 60)、且 Info 为空或只有 NULL 的行。这些大概率是应用拿了连接没归还,或者归还失败后连接还挂在 MySQL 侧。
- 执行
SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND = 'Sleep' AND TIME > 60;直接过滤 - 对比应用启动时间与
Time值:如果某连接的Time接近服务已运行秒数,基本可断定从未被释放 - 注意
Host列是否指向你的应用机器 IP,排除 DBA 手动连上去忘关的情况
Spring Boot + MyBatis 场景下最容易漏关连接的三个位置
框架封装越深,越容易掩盖底层连接生命周期。MyBatis 默认用 SqlSession 管理连接,而 Spring 的 @Transactional 会接管其生命周期——但一旦打破这个链路,就极易泄露。
- 手动创建
SqlSession(比如用sqlSessionFactory.openSession())却没调session.close():这是硬编码泄漏,和池子无关,但后果一样 - 在事务方法内用了
try-with-resources包裹Connection,但该 Connection 来自SqlSession.getConnection()—— 此时 close 会破坏 Spring 事务上下文,导致后续 SQL 报Transaction is not active,开发者一慌注释掉 close,泄漏就来了 - 异步任务(
@Async)里执行 DAO 操作:事务上下文不传递,连接不会自动绑定/释放,必须确保该方法自己管理连接或改用非事务方式
复杂点在于:泄漏往往不是单点 bug,而是多个条件叠加——比如一个异步方法里手动开了 session,又没关,再遇上超时重试,连接数就指数级上涨。别只盯着代码有没有 close,要盯住“谁申请、谁释放、在哪个线程、是否受事务控制”这四件事是否对得上。











