You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oracle R2DBC连接池耗尽:4次请求后超时问题排查求助

Oracle R2DBC连接池第5次请求超时排查方案

问题场景

使用Oracle R2DBC驱动连接数据库,配置代码如下:

ConnectionFactory connectionFactory = ConnectionFactories.get(ConnectionFactoryOptions.builder()
                                                              .option(HOST, host)
                                                              .option(PORT, port)
                                                              .option(USER, userName)
                                                              .option(PASSWORD, password)
                                                              .option(DRIVER, driver) // driver is pool
                                                              .option(DATABASE, service)
                                                              .option(PROTOCOL, protocol) // protocol is oracle
                                                              .build());

通过Flux.usingWhen执行查询:

Flux
    .usingWhen(con.getConnection(),
        connection -> connection.createStatement("SELECT QUERY").execute(),
        connection -> connection.close()) // 尝试归还连接到池
    .subscribeOn(Schedulers.boundedElastic())
    .flatMap(result -> result.map(row -> {
        ResultDto rto = new ResultDto(row.get("x", String.class), row.get("c", Double.class),
                row.get("y", String.class));
        return rto;
    }))
    .doOnError(e -> {
        throw new RuntimeException("DB error on select", e);
    })
    .collectList()
    .block();

前4次请求正常执行,第5次抛出超时错误:

reactor.core.Exceptions$ErrorCallbackNotImplemented: io.r2dbc.spi.R2dbcTimeoutException: Connection acquisition timed out after 10000ms
Caused by: io.r2dbc.spi.R2dbcTimeoutException: Connection acquisition timed out after 10000ms

排查与解决建议

1. 补充连接池核心配置参数

当前连接工厂仅配置基础连接信息,未指定连接池的最大连接数、超时等关键参数。默认情况下R2DBC连接池的最大连接数可能为4,导致前4次请求耗尽所有连接,第5次等待超时。需添加连接池相关配置:

ConnectionFactoryOptions.builder()
    // 原有基础参数...
    .option(ConnectionFactoryOptions.MAX_SIZE, 10) // 设置最大连接数,根据业务调整
    .option(ConnectionFactoryOptions.MIN_IDLE, 2) // 保持最小空闲连接数
    .option(ConnectionFactoryOptions.ACQUIRE_TIMEOUT, Duration.ofSeconds(15)) // 调整连接获取超时时间
    .option(ConnectionFactoryOptions.IDLE_TIMEOUT, Duration.ofMinutes(5)) // 空闲连接回收超时
    .build();

2. 确保连接释放逻辑可靠执行

虽然代码中调用了connection.close(),但需确认释放逻辑在所有场景下(成功、失败、取消)都能执行:

  • usingWhen的第三个参数必须返回Mono<Void>,确保连接关闭操作被正确订阅。如果connection.close()抛出异常,需捕获并处理,避免释放逻辑中断:
    connection -> Mono.from(connection.close())
        .onErrorResume(e -> {
            // 记录连接关闭失败的日志,避免影响主流程
            log.error("Failed to close connection", e);
            return Mono.empty();
        })
    
  • 避免在doOnError中直接抛出未捕获的异常,这可能导致usingWhen的释放逻辑无法触发。建议将错误处理改为返回错误信号,而非直接抛出:
    .doOnError(e -> log.error("DB error on select", e))
    

3. 确保结果集被完全消费

R2DBC的Result对象需要被完全遍历消费,否则连接无法被正确归还到池。当前代码中flatMap处理Result时,应使用flatMapMany确保整个结果集被消费:

.flatMapMany(result -> result.map(row -> {
    ResultDto rto = new ResultDto(row.get("x", String.class), row.get("c", Double.class),
            row.get("y", String.class));
    return rto;
}))

如果结果集未被完全消费,连接会被挂起,逐渐耗尽连接池资源。

4. 避免阻塞调用与线程模型冲突

在Mule应用中使用block()会阻塞当前线程,可能导致线程池耗尽或连接释放逻辑延迟。建议改用异步方式返回Mono<List<ResultDto>>,由Mule的反应式框架处理订阅:

return Flux
    .usingWhen(con.getConnection(),
        connection -> connection.createStatement("SELECT QUERY").execute(),
        connection -> Mono.from(connection.close()).onErrorResume(e -> {
            log.error("Close connection failed", e);
            return Mono.empty();
        }))
    .subscribeOn(Schedulers.boundedElastic())
    .flatMapMany(result -> result.map(row -> new ResultDto(
            row.get("x", String.class),
            row.get("c", Double.class),
            row.get("y", String.class))))
    .doOnError(e -> log.error("DB query error", e))
    .collectList();

5. 检查数据库端会话限制

确认Oracle数据库对当前用户的并发会话数限制:

  1. 查询用户对应的profile:
    SELECT username, profile FROM dba_users WHERE username = 'YOUR_USERNAME';
    
  2. 查询该profile的会话数限制:
    SELECT resource_name, limit 
    FROM dba_profiles 
    WHERE profile = 'YOUR_PROFILE_NAME' 
      AND resource_name = 'SESSIONS_PER_USER';
    

如果限制为4,需调整数据库配置或用户profile的会话数上限。

内容的提问来源于stack exchange,提问作者Isuru Samaraweera

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 13:16:15