本质是连接被卡住未归还,而非数量不足;需切断阻塞链、严控生命周期、强制超时熔断,禁用同步jdbc裸跑虚拟线程,优先用r2dbc或completablefuture解耦db操作,配小而稳专用线程池,设connection-timeout=3000、事务timeout=3、leakdetectionthreshold=60000,并监控active=idle+waiting≤max-total。

高并发下 JDBC 连接池爆满,本质不是“连接不够用”,而是连接被卡住、没归还、或被错误复用。解决重点不在加 max-active,而在切断阻塞链、控制生命周期、强制超时熔断。
禁用同步 JDBC 在流式/虚拟线程场景中裸奔
传统 JDBC 的 executeQuery() 是同步阻塞调用,哪怕跑在 Spring Boot 3.4+ 的虚拟线程里,也会让载体线程挂起不动——连接不释放、线程不退出,形成死锁闭环。
- Spring Boot 3.4+ 项目中,停用 JdbcTemplate 和 MyBatis 同步模式,改用 R2DBC(如 r2dbc-postgresql)或封装好的异步客户端
- 若必须用 JDBC,把 DB 操作彻底移出主线程:用 CompletableFuture.supplyAsync(() -> dao.find(id), dbExecutor) 拆离,并为 dbExecutor 单独配小而稳的线程池(core=4,max=8,队列有界)
- 绝对避免在 parallelStream 中直接调用 DAO 方法,也不要把 Connection 或 PreparedStatement 作为参数跨 stage 传递
收紧连接与操作的超时边界
没有超时的数据库调用,等于给连接池埋雷。一个慢查询卡住 10 秒,可能拖垮整个并发流。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- HikariCP 配置 connection-timeout=3000:获取连接超 3 秒就失败,不排队等待
- SQL 层加执行时限:MySQL 用 SET STATEMENT max_execution_time = 2000 FOR SELECT ...;MyBatis 的
<select timeout="2"></select>显式声明秒级超时 - @Transactional 注解必须带 timeout = 3,防止事务空悬;autocommit=false 场景下,无显式 commit/rollback 就是隐患
确保每个 DB 操作独立完成生命周期
常见错误是拿一个 Connection 贯穿 map/filter/flatMap,或在多个 parallelStream 线程中复用同一 DataSource 获取的连接——轻则连接泄漏,重则并发操作 Connection 抛异常。
- 每条数据库操作都应独立获取连接、执行、关闭:优先使用 try-with-resources,或确保 flatMap 内部完整走完 open → execute → close
- HikariCP 必开 leakDetectionThreshold=60000(60 秒),一旦连接未归还即报 warn,避免静默吃光池子
- 监控关键指标:活跃连接数、等待线程数、空闲连接数。可通过
HikariPoolMXBean实时输出:active=idle+waiting应始终 ≤ max-total
连接池本身要合理配置而非盲目扩容
盲目调大 max-active 只会把压力转嫁给数据库,甚至触发 MySQL 的 max_connections 限制,引发 “too many connections” 错误。
- 初始配置参考:min-idle=5,max-active=20(视 DB 实例规格调整),initial-size=5,避免冷启动抖动
- 设置 max-wait-millis=3000,配合 connection-timeout 使用,让请求快速失败而非无限排队
- 启用 remove-abandoned-on-borrow=true(HikariCP 5.0+ 改为
abandoned-timeout),自动回收疑似泄漏的连接
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










