生产高峰期偶现报错: could not get JDBC Connection; nested exception is com.zaxxer.hikari.pool.HikariPool$PoolTimeoutException: HikariPool‑1‑ Connection is not available
连接池耗尽,大量接口阻塞超时,重启服务临时恢复,一段时间之后再次复现。
SpringBoot3 默认依旧使用 HikariCP,但在 JDK17 模块化、虚拟线程、新的异常栈、资源关闭行为上,和 JDK8 存在细微差异;同时很多人升级之后直接沿用老 JDK8 的连接池配置,会埋下隐患。
不少同学遇到报错第一反应:直接调大maximum‑pool‑size。 盲目放大连接数只是治标不治本。真正根源集中在:连接泄露、长事务霸占连接、慢 SQL、线程模型与连接池不匹配,虚拟线程使用不当带来的新问题。
本文基于 SpringBoot3 + JDK17,结合线上真实故障,梳理高频场景、错误 Demo、修复方案、生产编码检查清单。
前置:SpringBoot3.x 强制依赖 HikariCP;JDK17 强化
AutoCloseable资源关闭,同时引入虚拟线程,给数据库连接管理带来新的注意点。
一、场景 1:手动获取 Connection,异常分支未关闭,发生连接泄露
即使在 JDK17,手动获取连接,如果异常、提前 return 绕过关闭逻辑,依旧会发生连接泄露。 JDK17 不会自动帮你回收数据库连接资源。
❌错误 Demo(升级项目经常遗留老 JDK8 写法)
ini
@Service
public class StudentDbService {
@Autowired
private DataSource dataSource;
public void queryStudent() throws SQLException {
Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement("select * from t_student");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
//业务处理
}
//异常抛出,直接跳出方法,后面close完全不会执行,连接泄露
int i = 1 / 0;
rs.close();
ps.close();
connection.close();
}
}
现象:每调用一次消耗一条数据库连接,连接池被慢慢吃满,最终报拿不到连接。
✅JDK7 + 推荐:try‑with‑resources,JDK17 下对 AutoCloseable 支持更完善,自动释放资源
ini
public void queryStudent() throws SQLException {
try (Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement("select * from t_student");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
//业务处理
}
int i = 1 / 0;
}
}
生产准则:业务代码尽量不要手动 getConnection ,交给 MyBatis‑SpringBoot3 适配包管理连接生命周期。 如果迫不得已手动操作连接,先写好获取与回收闭环,再写业务逻辑 ;优先回调模板模式,业务逻辑写进回调,避免
return、异常、if‑else 分支绕过资源回收。
二、场景 2:长事务霸占连接,SpringBoot3 环境依旧高频踩坑
@Transactional方法内部嵌入 RPC、HTTP 调用、重量级 IO,事务未提交,数据库连接一直被占用。
❌错误 Demo
java
@Service
public class StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private RemoteFeign remoteFeign;
@Transactional(rollbackFor = Exception.class)
public void updateStudent() {
studentMapper.updateInfo(new Student());
// Feign远程调用,网络抖动阻塞,连接长期占用不释放
remoteFeign.callThirdApi();
}
}
SpringBoot3 的
@Transactional底层逻辑没有改变:进入事务方法就获取连接,方法执行完毕才归还。
✅修复:远程调用移出事务;耗时非数据库操作可考虑队列异步处理
csharp
public void updateStudent() {
//远程调用放到事务外部
remoteFeign.callThirdApi();
doDbUpdate();
}
@Transactional(rollbackFor = Exception.class)
public void doDbUpdate() {
studentMapper.updateInfo(new Student());
}
重要提醒:不是所有长事务都直接丢队列。如果多步数据库操作必须原子性,保留短事务,只把 RPC、IO、计算移出事务。队列解决的是耗时外部调用,不能替代数据库事务原子性。
三、场景 3:慢 SQL 长时间占用连接,JDK17 不解决 SQL 本身性能问题
升级 JDK 版本不会优化业务 SQL。缺失索引、复杂大查询,单条 SQL 执行几十秒,持续霸占连接。
排查手段:
- MySQL 执行
show processlist观察长时间 Running 会话; - SpringBoot3 Actuator 暴露 Hikari 连接池指标,监控活跃连接、连接持有时长。
application‑yaml 片段
yaml
spring:
datasource:
hikari:
connection-timeout: 30000
management:
endpoints:
web:
exposure:
include: metrics
✅处理:SQL 加索引优化;设置合理超时,禁止 SQL 无限阻塞。
四、场景 4:SpringBoot3 + JDK17 虚拟线程带来的新坑
JDK21 虚拟线程,SpringBoot3.2 + 支持虚拟线程。很多同学直接全部接口切虚拟线程,这里有连接池关键认知。
虚拟线程本身不消耗数据库连接池,虚拟线程多,并不代表可以无限放大 HikariCP 最大连接数。 大量虚拟线程同时访问数据库,依旧会排队等待有限的数据库连接。
误区:开启虚拟线程,直接把maximum‑pool‑size拉很大。
MySQL 服务端连接数上限有限,连接数过高会造成数据库上下文切换加剧、锁竞争,整体性能下降。
✅连接池大小参考原则 网上流传公式:maximum‑pool‑size = CPU核心数 *2 + IO数量
⚠️这只是初始参考值,不是固定标准答案。
- CPU 密集、SQL 执行很快,可以贴近公式;
- IO 等待多、慢查询多,适度调大;
- 多实例部署,需要除以实例数量,不能超过 MySQL 全局最大连接上限。
SpringBoot3 + JDK17 业务服务参考初始配置
yaml
spring:
datasource:
hikari:
maximum-pool-size: 12
minimum-idle: 4
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
虚拟线程场景建议:业务线程是虚拟线程,Hikari 连接池依旧需要合理限制,不能放任膨胀。
五、SpringBoot3 + JDK17 环境故障排查手段
- 日志关键词:
PoolTimeoutException,重点检索连接拿不到异常栈; - 开启 Actuator,监控 hikari 相关 metrics,观察活跃连接、等待队列;
- MySQL
show processlist查看长时间运行会话; - 代码审计:是否手动获取 Connection,资源是否闭环释放;
- 检查
@Transactional内部是否存在 RPC、HTTP、大 IO; - 如果开启虚拟线程,核对虚拟线程数量和数据库连接池配比。
六、生产编码 CheckList(适配 JDK17+SpringBoot3)
- ✅连接池
maximum‑pool‑size,CPU*2+IO 数仅作为初始参考,不是固定值;上线结合监控指标调优,同时兼顾 MySQL 总连接数与服务实例数; - ✅事务内部严禁 RPC、Feign 调用、大文件 IO、重量级计算;耗时逻辑移出事务,必要时队列异步;必须原子性的数据库操作保留短事务,不盲目队列化;
- ✅业务代码尽量不手动获取
Connection,交给 MyBatis 等框架管理连接生命周期; - ✅迫不得已手动操作连接时,先完成【获取‑异常‑回收】闭环,再写业务逻辑;优先
try‑with‑resources或者回调模板,业务写进回调,防止分支逻辑绕过资源回收; - ✅开启 Actuator 监控 Hikari 连接池指标,线上配置活跃连接阈值告警;
- ✅使用 JDK21 虚拟线程,不要误以为虚拟线程可以无视数据库连接池上限;
- ✅升级 SpringBoot3/JDK17,不要直接照搬 JDK8 时代全套连接池配置,重新做压测验证。
七、AI 代码的坑
AI 生成数据库操作 Demo,经常只写正常流程,忽略异常、提前 return 场景。 JDK17 虽然资源关闭能力增强,但不会自动修复业务代码的资源泄露。复制 AI 数据库相关代码,务必审查资源回收闭环。
升级 SpringBoot3 + JDK17 会带来虚拟线程、模块化、性能提升,但不会自动修复长事务、连接泄露、SQL 慢查询这类业务问题 。 遇到could not get JDBC Connection报错,不要上来就调大最大连接数。优先排查:连接泄露、长事务、慢 SQL、线程模型与连接池的匹配,开启虚拟线程后要额外注意连接池约束。
我是十年 Java 后端程序媛,分享 JDK 迁移、业务踩坑实录,完整文章合集同步更新公众号:Java 后端程序媛手记。
下一篇分享 SpringBoot3 虚拟线程与数据库连接池配合实战。 如果对你有帮助,欢迎点赞收藏,关注我不错过后续更新。