Spring JdbcClient致Oracle会话过多及HikariCP连接超时问题求助
一、DriverManagerDataSource 会话激增问题
DriverManagerDataSource本身不支持连接池,每次调用JdbcClient时都会创建新的物理数据库连接——它只是简单封装了DriverManager,每次请求新建连接、用完直接关闭物理连接而非放回池,高并发下必然导致会话数飙升至4k,引发资源耗尽。
解决办法:直接弃用DriverManagerDataSource,使用HikariCP(Spring Boot默认连接池)或其他成熟连接池,这是唯一可行的方案。
二、HikariCP 连接超时问题(Connection is not available)
你怀疑连接未被正确释放是合理的,结合代码片段来看,问题大概率出在Stream资源未关闭上。
问题根源
jdbcClient.query(...).stream()返回的Stream绑定了数据库连接,仅调用findFirst()时,若Stream提前终止或发生异常,连接可能无法自动释放。HikariCP的连接被持续占用无法回池,后续请求拿不到连接最终超时。
解决方案
1. 用try-with-resources确保Stream关闭
通过try-with-resources语法自动关闭Stream,强制释放连接回池:
@Autowired private JdbcClient jdbcClient; public String getQueryResult(String id) { try (Stream<String> stream = jdbcClient.sql("query") .param("id", id) .query(String.class) .stream()) { return stream.findFirst().orElse(""); } }
try-with-resources会自动关闭实现AutoCloseable接口的资源,这里关闭Stream时会同步释放绑定的数据库连接。
2. 检查HikariCP配置合理性
若上述修改后问题仍存在,排查以下配置:
spring.datasource.hikari.maximum-pool-size:若设置过小,无法应对并发请求,建议按服务器CPU核数调整(例如核数*2+1)spring.datasource.hikari.leak-detection-threshold:开启连接泄漏检测,设置为2000毫秒,日志会打印泄漏连接的调用栈,帮助定位问题:spring.datasource.hikari.leak-detection-threshold=2000
3. 确认JdbcClient绑定正确数据源
确保JdbcClient基于HikariDataSource创建,而非误用DriverManagerDataSource。Spring Boot默认会自动创建HikariDataSource,若自定义数据源配置,需明确返回HikariDataSource:
@Configuration public class DataSourceConfig { @Bean public DataSource dataSource(Environment env) { HikariDataSource dataSource = new HikariDataSource(); dataSource.setJdbcUrl(env.getProperty("spring.datasource.url")); dataSource.setUsername(env.getProperty("spring.datasource.username")); dataSource.setPassword(env.getProperty("spring.datasource.password")); dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name")); // 其他Hikari配置 return dataSource; } @Bean public JdbcClient jdbcClient(DataSource dataSource) { return JdbcClient.create(dataSource); } }
4. 排查Oracle会话状态
用Oracle语句查看会话状态,确认连接是否被占用未释放:
SELECT s.sid, s.serial#, s.status, s.username, s.program FROM v$session s WHERE s.username = 'YOUR_DB_USER';
若存在大量ACTIVE状态且归属你的应用的会话,说明确实存在连接泄漏,结合泄漏检测日志定位代码。
内容的提问来源于stack exchange,提问作者Mustafa zuhair

