数据库连接池该怎么配置才能避免连接泄露 置顶 精华 推荐
结论:避免连接泄露靠三件事,不是靠把池子调大——① 代码里 100% 用 try-with-resources 或 finally 归还连接;② 设置 connectionTimeout,让拿不到连接时快速失败而不是无限等待;③ 在测试环境打开泄露检测(HikariCP 的 leakDetectionThreshold 设为 2000~6000ms),让它直接打出泄露位置的堆栈。
连接泄露到底是怎么发生的
结论:绝大多数泄露不是连接池的锅,是业务代码在某条异常路径上没归还连接,池子里的空闲连接被一点点吃光,最后卡在「获取连接超时」。
最常见的 5 种写法:一是 Connection conn = ds.getConnection(); 之后中间 return 或抛异常,没有 finally 兜底;二是把 Connection 塞进 ThreadLocal 缓存复用,线程复用后忘了 remove();三是 Spring 的 @Transactional 方法里调用了耗时几秒的 HTTP/RPC,事务没提交,连接一直挂在该线程上;四是 MyBatis 里手写 SqlSession 却没 close();五是只关 Connection 不关 Statement/ResultSet——HikariCP 关连接时会连带回收,但自己包装的 DataSource 不一定。
还有一个隐蔽场景:慢 SQL。一条跑 60 秒的查询等同于一条连接被占用 60 秒,10 条并发慢 SQL 就能打满默认 10 个连接的池子,现象和泄露一模一样。
连接池参数怎么配,给一套能直接抄的模板
结论:HikariCP 里真正和「防泄露」相关的是 4 个参数——connectionTimeout、leakDetectionThreshold、maxLifetime、maximumPoolSize,其余保持默认即可。
HikariCP 5.x 的默认值是:maximumPoolSize=10、minimumIdle=10、connectionTimeout=30000(毫秒,最小可设 250)、idleTimeout=600000(10 分钟)、maxLifetime=1800000(30 分钟)、leakDetectionThreshold=0(即关闭)。
推荐配置:
spring.datasource.hikari.maximumPoolSize=20
spring.datasource.hikari.connectionTimeout=3000
spring.datasource.hikari.maxLifetime=1740000
spring.datasource.hikari.leakDetectionThreshold=6000
说明几点:connectionTimeout 从默认 30 秒降到 3 秒,是为了让「池子被占满」这件事立刻暴露成异常,而不是把请求全部堆在等待队列里;maxLifetime 设 1740000ms(29 分钟),要比数据库的 wait_timeout 至少小 30 秒——MySQL 默认 wait_timeout=28800 秒且 HikariCP 默认 30 分钟本来就安全,但如果 DBA 把它调成了 60 秒,就必须跟着改;leakDetectionThreshold 最小有效值是 2000ms,小于这个数会被忽略。
如果用 Druid,对应的四个参数是 removeAbandoned=true、removeAbandonedTimeout=60(秒)、logAbandoned=true、maxWait=3000,另外务必别用 maxWait=-1(默认值,无限等待)。注意 removeAbandoned 会强制回收连接,生产环境开启有性能代价,建议只在测试/预发打开。
为什么 try-with-resources 比调大连接池更管用
结论:调大 maximumPoolSize 只是把「多久暴露问题」从 5 分钟拖到 30 分钟,泄露速率不变;try-with-resources 才是把泄露源头堵死。
正确写法只有一种:
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, id);
try (ResultSet rs = ps.executeQuery()) { /* ... */ }
}
Connection、Statement、ResultSet 都实现了 AutoCloseable,离开 try 块时按逆序自动关闭,即使中间抛异常也一样。
配套的两条规则:事务方法里不要做网络调用——把 RPC、发消息、文件上传挪到事务提交之后;不要在 Service 里嵌套开启新事务,Spring 默认传播行为 REQUIRED 会复用同一个连接,嵌套 REQUIRES_NEW 则会同时占用两个连接,高并发下直接翻倍消耗池子。
高并发下 maximumPoolSize 设多少
结论:默认的 10 通常够用,盲目设成 100 反而更慢;按公式估算,8 核机器配 17~20 个连接是合理区间。
PostgreSQL 官方 wiki 给的公式是 连接数 = CPU 核数 × 2 + 有效磁盘数,SSD 场景下常用 核数 × 2 + 1。原因是连接数超过 CPU 并行处理能力后,线程切换和数据库侧的锁竞争会让吞吐下降、响应时间上升。
上线后怎么监控连接泄露
结论:盯两个指标就够——活跃连接数 active 和等待连接的线程数 pending,前者持续贴近最大值、后者长期大于 0,就是泄露或池子太小。
HikariCP 可以通过 HikariPoolMXBean 拿到 getActiveConnections() 和 getThreadsAwaitingConnection(),接到 Prometheus/Micrometer 里做告警;Druid 自带监控页,能看到活跃连接、等待线程数、以及被 removeAbandoned 回收的次数,回收次数持续增长就是明确的泄露信号。
一句话收尾:连接泄露是代码纪律问题,池子参数只能帮你更快发现它——try-with-resources 兜住归还、connectionTimeout 兜住等待、leakDetectionThreshold 兜住排查,这三条做到,泄露基本不会积累成故障。
原文链接:https://www.gj0.com/thread-263.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。