Spring Boot+HikariPool无法获取JDBC Connection问题求助
解决Spring Boot+Hikari连接池无法获取JDBC连接的问题
问题根源分析
你的代码和配置存在几个核心问题,直接导致连接池耗尽:
- 无限制创建线程:在while循环中每次都
new Thread(),循环次数超500次时会生成大量线程,每个线程都要抢占数据库连接,直接打满连接池。 - 连接持有时间过长:API调用属于高耗时操作,却和数据库save操作放在同一线程的事务流程中,导致连接被长时间占用无法释放。
- 事务范围不合理:
@Transactional加在类上时,会让包含耗时API调用的整个方法都处于事务中,进一步延长连接持有周期。 - 数据库端连接限制:PostgreSQL默认有
max_connections配置,若Hikari的maximum-pool-size超过该值,即使调大池大小也无济于事。
具体解决方案
1. 用线程池替代手动创建线程
不要直接new Thread(),通过线程池控制并发数,避免瞬间创建大量线程耗尽连接:
// 定义线程池(可使用Spring的ThreadPoolTaskExecutor,或手动创建) private ExecutorService executor = Executors.newFixedThreadPool(10); // 根据业务场景调整核心线程数 int count = programRepository.countBlackoutPending(); while (count > 0) { // ... executor.submit(() -> { // 业务逻辑 }); // 每次循环后重新获取count,避免无限循环 count = programRepository.countBlackoutPending(); }
2. 分离耗时操作与数据库操作,缩短连接持有时间
把API调用这类非数据库操作移出连接/事务的持有周期,先查询数据、释放连接,再处理API,最后批量更新:
// 1. 查询数据(获取连接→查询完成后立即释放) List<ProgramEntity> programList = programRepository.findAllByID(); // 2. 执行API调用(此时不占用数据库连接) programList.forEach(program -> { // API调用逻辑 }); // 3. 批量保存更新(一次性获取连接,批量操作后释放) programRepository.saveAll(programList);
3. 优化事务注解的使用
不要将@Transactional加在包含耗时API调用的方法上,仅在纯数据库操作的方法上添加:
// 仅在数据库操作方法上加事务 @Transactional public void savePrograms(List<ProgramEntity> programs) { programRepository.saveAll(programs); } // 业务方法中调用,API调用不在事务范围内 public void processPrograms() { List<ProgramEntity> list = programRepository.findAllByID(); // API调用逻辑 savePrograms(list); }
4. 检查并调整连接池与数据库配置
- 检查PostgreSQL连接限制:执行SQL命令
show max_connections;查看数据库允许的最大连接数,Hikari的maximum-pool-size必须小于等于该值(建议预留10-20个连接给数据库管理员及其他系统)。 - 开启Hikari连接泄漏检测:在
application.properties中添加配置,排查连接未释放的情况:
spring.datasource.hikari.leak-detection-threshold=60000 # 60秒检测连接泄漏
- 优化连接池其他参数:合理设置闲置超时、最小空闲连接数,避免连接闲置过久或数量不足:
spring.datasource.hikari.minimum-idle=20 spring.datasource.hikari.idle-timeout=300000
5. 修复循环逻辑bug
确保while循环中每次都会重新获取count值,否则会陷入无限循环,不断创建线程耗尽资源:
int count = programRepository.countBlackoutPending(); while (count > 0) { // 处理逻辑 // 必须重新查询count,否则循环无法终止 count = programRepository.countBlackoutPending(); }
内容的提问来源于stack exchange,提问作者M.A.Murali
相关产品推荐
相关产品推荐

