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数据库对当前用户的并发会话数限制:
- 查询用户对应的profile:
SELECT username, profile FROM dba_users WHERE username = 'YOUR_USERNAME'; - 查询该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
相关产品推荐
相关产品推荐

