You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Apache Commons DBCP2连接池出现超时错误(Cannot get a connection, pool error Timeout waiting for idle object)的修复求助

Apache Commons DBCP2连接池出现超时错误(Cannot get a connection, pool error Timeout waiting for idle object)的修复求助

看起来你遇到了连接池资源耗尽的棘手问题,明明调用了con.close()想要归还连接,但循环到第9次之后还是触发了超时错误,咱们一步步来排查和修复:

1. 致命问题:每次获取连接都新建了一个连接池实例

看你的getConnection()方法,每次调用都会执行new BasicDataSource()——这意味着每个请求都在创建一个全新的连接池,而不是复用同一个全局池!每个新池的maxTotal=8,但这些池之间的连接完全不共享,旧池的连接会因为没有引用被慢慢回收,但回收速度远远赶不上你循环创建新池的速度,很快就会耗尽数据库的总连接数,导致后续请求超时。

修复方案:把BasicDataSource改成单例,全局只初始化一次

// 全局单例的连接池实例
private static BasicDataSource dataSource;

// 静态代码块只在类加载时执行一次,初始化连接池
static {
    dataSource = new BasicDataSource();
    dataSource.setDriverClassName(JDBC_DRIVER);
    dataSource.setUrl(db.url);
    dataSource.setUsername(db.username);
    dataSource.setPassword(db.password);
    dataSource.setInitialSize(2);
    dataSource.setMaxTotal(8);
    dataSource.setLogAbandoned(true);
    dataSource.setMinIdle(2);
    dataSource.setMaxWaitMillis(10000);
    dataSource.setTestOnReturn(true);
    dataSource.setValidationQuery("SELECT 1");
    // 这个值太小了,会导致回收线程疯狂运行,建议改成30秒(30000毫秒)
    dataSource.setTimeBetweenEvictionRunsMillis(30000);
}

public Connection getConnection(){
    return dataSource.getConnection();
}

2. 资源未正确关闭,导致连接无法归还

你的select_sql方法里只关闭了Connection,但PreparedStatementResultSet完全没处理。这些资源如果不关闭,连接池会认为当前连接还在被占用,无法正确归还到池中;而且close()方法可能抛出异常,导致后续的关闭操作直接跳过,进一步加剧资源泄漏。

修复方案:在finally块中按逆序关闭所有资源,每个操作单独捕获异常

public static JSONArray select_sql(String sql){
    Connection con = null;
    ResultSet rs = null;
    PreparedStatement ps = null;
    try {
        con = DataBasePool.getConnection(systemName);
        ps = con.prepareStatement(sql);
        // 注意:这里不要传sql参数,ps已经预编译过了,传参会重新创建语句,浪费资源
        rs = ps.executeQuery(); 
        ResultSetMetaData metaData = rs.getMetaData();
        while (rs.next()){
            // 你的业务逻辑
        }
        // 返回处理后的JSONArray
    } catch (Exception e) {
        // 注意拼写:是printStackTrace,不是printStrace
        e.printStackTrace(); 
    } finally {
        // 按ResultSet → PreparedStatement → Connection的顺序关闭(创建顺序的逆序)
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (con != null) {
            try {
                con.close(); // 这里才会真正把连接归还到池里
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    // 返回默认结果或处理后的数组
}

3. 连接池参数优化

你设置的timeBetweenEvictionRunsMillis=2毫秒,这会导致连接池的空闲连接回收线程每秒运行500次,占用大量CPU资源,严重影响池的正常运作。建议改成30000毫秒(30秒),或者根据你的业务场景调整为合理的间隔。

最后总结

先修复连接池单例的问题(这是核心原因),再确保所有数据库资源都被正确关闭,最后优化参数。这样循环15次的时候,连接会被正确归还并复用,就不会再出现超时错误了。

备注:内容来源于stack exchange,提问作者woxiangbo

火山引擎 最新活动