摘要:在数据库连接池的使用中,连接泄漏是一个常见且严重的问题。本文通过分析一个实际的案例,探讨了连接泄漏的危害、产生原因以及如何在 Druid 和 HikariCP 这两种常见的连接池中定位和解决连接泄漏问题。
背景
最近碰到一个 case,一个 Java 应用无法获取新的数据库连接,日志中出现了以下错误:
com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 5001, active 20, maxActive 20, creating 0
at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1894)
at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1502)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1482)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1463)
active 等于 maxActive,说明连接池中的连接已耗尽。
分析报错时间段的数据库连接情况,发现数据库的连接数(Threads_connected)显著增加,但活跃线程数(Threads_running)较低且平稳。活跃线程数低且平稳意味着没有慢查询占用连接。但连接数增加明显,说明连接未被及时释放回连接池。
对于这种在一定时间内没有进行任何操作,但又未及时归还到连接池的连接,其实有个专用名词,即泄漏连接(Leaked Connection)。
下面,我们聊聊泄漏连接的相关问题,包括:
- 泄漏连接的危害。
- 泄漏连接的产生原因。
- Druid 中如何定位泄漏连接。
- HikariCP 中如何定位泄漏连接。
泄漏连接的危害
泄漏连接可能引发以下问题:
-
连接池耗尽:泄漏的连接会持续占用连接池中的资源,导致可用连接逐渐减少,最终耗尽连接池。
-
应用性能下降:当连接池中的连接被耗尽时,新的数据库操作无法获取连接,导致请求阻塞或失败,这可能导致应用程序无法正常运行。
-
数据库资源浪费:泄漏的连接会占用数据库的连接资源,可能导致数据库的连接数达到上限。
-
连接失效风险:长时间未释放的连接无法通过连接池的 Keep-Alive 机制保持活跃,更容易因空闲超时被 MySQL 服务端或中间件关闭。
当使用这些已关闭的连接执行数据库操作时,会触发经典的 “Communications link failure. The last packet successfully received from the server was xxx milliseconds ago.” 错误。
泄漏连接的产生原因
泄漏连接通常由以下原因导致:
1.长事务或长连接。
事务长时间未提交或连接长时间未释放。
2.未关闭连接。
在使用完连接后,未调用close()方法将连接归还到连接池。如,
Connection conn = dataSource.getConnection();
// 执行数据库操作
// 忘记调用 conn.close();
3.异常未处理。
在数据库操作过程中发生异常,导致连接未正常关闭。如,
Connection conn = null;
try {
conn = dataSource.getConnection();
// 执行数据库操作
throw new RuntimeException("模拟异常");
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close(); // 异常发生后,可能不会执行到此处
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Druid 中如何定位泄漏连接
在 Druid 连接池中,可以通过以下参数开启未归还连接的检测:
- removeAbandoned:是否回收超时未归还的连接,默认值为 false,表示不回收。
- removeAbandonedTimeoutMillis:未归还连接的超时时间(单位:毫秒)。默认值为 300000(即 300 秒)。
- logAbandoned:是否将超时未归还的连接信息打印到日志中。默认值为 false,表示不打印。
需要注意的是,logAbandoned 仅在 removeAbandoned 为 true 时生效。也就是说,Druid 连接池不支持仅打印,但不回收超时未归还连接的功能。
实现细节
在从连接池获取连接时,如果removeAbandoned为 true,则会记录连接的堆栈信息和创建时间,用于检测未归还连接。
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {

最低0.47元/天 解锁文章
2779

被折叠的 条评论
为什么被折叠?



