如何定位 Druid & HikariCP 连接池的连接泄漏问题?

摘要:在数据库连接池的使用中,连接泄漏是一个常见且严重的问题。本文通过分析一个实际的案例,探讨了连接泄漏的危害、产生原因以及如何在 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)。

下面,我们聊聊泄漏连接的相关问题,包括:

  1. 泄漏连接的危害。
  2. 泄漏连接的产生原因。
  3. Druid 中如何定位泄漏连接。
  4. HikariCP 中如何定位泄漏连接。

泄漏连接的危害

泄漏连接可能引发以下问题:

  1. 连接池耗尽:泄漏的连接会持续占用连接池中的资源,导致可用连接逐渐减少,最终耗尽连接池。

  2. 应用性能下降:当连接池中的连接被耗尽时,新的数据库操作无法获取连接,导致请求阻塞或失败,这可能导致应用程序无法正常运行。

  3. 数据库资源浪费:泄漏的连接会占用数据库的连接资源,可能导致数据库的连接数达到上限。

  4. 连接失效风险:长时间未释放的连接无法通过连接池的 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 {
   
   
### 比较JDBC连接池DruidHikariCP的优缺点 #### Druid的优点 1. **丰富的监控功能** Druid提供了强大的监控能力,能够实时统计SQL执行的时间、次数以及慢查询日志等功能。这对于优化数据库访问效率非常有帮助。 2. **内置过滤器机制** 支持自定义Filter接口实现各种需求,比如读写分离、分库分表等复杂场景下的应用扩展[^1]。 3. **安全性考虑** 提供密码加密存储特性,在一定程度上增强了敏感信息的安全防护措施。 4. **配置灵活性高** 可以方便地调整参数设置来适应不同业务环境的要求,如最大活跃数、最小闲置数量等属性都可以灵活设定。 #### Druid的缺点 1. **性能相对较低** 由于其内部实现了较多的功能模块,默认情况下开启了很多不必要的服务项(例如StatFilter),这可能会造成一定的资源消耗并影响整体表现速度。 2. **依赖关系复杂** 集成了许多额外组件使得项目结构变得臃肿,增加了维护成本和技术栈的学习曲线。 #### HikariCP的优点 1. **卓越的性能表现** 经过精心设计和调优后的高性能连接池方案之一,官方宣称比其他同类产品快约7到20倍不等,尤其适合高并发请求场景下使用。 2. **轻量级架构** 不含任何第三方依赖包,保持了极简的设计理念;源码简洁易懂便于二次开发人员理解掌握核心逻辑。 3. **自动清除陈旧连接** 能够有效识别长时间未被使用的Connection对象,并及时回收释放内存空间防止泄露问题发生。 4. **良好的社区支持** 开发者群体庞大且活跃度较高,遇到困难时容易获取外部援助或者借鉴他人经验解决问题。 #### HikariCP的缺点 1. **缺少高级特性** 和Druid相比缺乏一些企业级应用场景所需的附加功能,像详细的运行状态跟踪记录等方面有所欠缺。 2. **定制化程度有限** 对于某些特殊要求可能无法满足,因为本身定位就是追求极致的速度而舍弃了一部分可选配置选项。 ```java // 使用HikariCP创建DataSource实例 HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); config.setUsername("root"); config.setPassword("password"); HikariDataSource ds = new HikariDataSource(config); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值