Druid 是一个开源的大规模实时数据流处理和分析系统,由阿里巴巴集团开发。以下是定位 Druid 的几个关键点:1. **实时数据处理**: - Druid 能够处理和分析实时数据流,适用于需要即时洞察的场景,如实时广告点击、股票交易监控等。2. **高
摘要:在数据库连接池的使用中,连接泄漏是一个常见且严重的问题。本文通过分析一个实际的案例,探讨了连接泄漏的危害、产生原因以及如何在 Druid 和 HikariCP 这两种常见的连接池中定位和解决连接泄漏问题。
背景
最近碰到一个 case,一个 Java 应用无法获取新的数据库连接,日志中出现了以下错误:
com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis5001, active20, maxActive20, creating0
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();
// 执行数据库操作
thrownewRuntimeException("模拟异常");
}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,表示不打印。
