这是数据库连接池资源耗尽问题,而非JVM内存泄漏;因@Transactional方法内执行耗时操作(如远程调用、大文件处理等)导致连接长期被占用,无法归还池中,引发请求超时与线程阻塞。
这不是典型的“内存泄漏”,而是数据库连接被长时间占用,导致连接池资源耗尽、线程阻塞、后续请求堆积。spring 声明式事务(@transactional)本身不会直接造成 jvm 堆内存泄漏,但它会延长数据库连接的持有时间——如果事务方法里执行了超长耗时操作(如大文件处理、外部 http 调用、复杂计算、同步等待等),连接就一直被绑定在线程上,无法归还连接池。这会表现为连接池“假性泄露”:连接数打满、请求超时、hikari 日志频繁报 connection is not available, request timed out。
确认是否为事务内耗时操作导致连接积压
先排除数据库本身慢、网络延迟或连接池配置过小等干扰因素:
- 查日志中是否有
Connection is not available, request timed out after XXXms,且超时时间远大于业务正常响应(如设为30s,但实际接口平均耗时仅200ms) - 比对同一数据库的其他非事务接口或简单查询,是否响应正常——若只有带
@Transactional的接口卡顿,高度可疑 - 用
jstack <pid>抓线程快照,搜索java.lang.Thread.State: RUNNABLE并包含你事务方法名的堆栈,确认线程确实在执行耗时逻辑而非等待 DB 响应
定位具体哪个 @Transactional 方法在干“脏活”
声明式事务的边界是方法级,只要方法没返回,连接就一直被持有。重点排查:
- 所有
@Transactional方法体内是否调用了Thread.sleep()、TimeUnit.SECONDS.sleep()等显式等待 - 是否在事务中发起同步远程调用(如
RestTemplate.exchange()、FeignClient),且对方响应极慢或不可达 - 是否在事务中读写大文件、解析超大 Excel/JSON、做密集型计算(如循环百万次加法)
- 是否误将本该在事务外执行的初始化、校验、通知等逻辑塞进了事务方法里
验证连接持有时间与事务持续时间是否一致
启用 Hikari 连接池详细日志,观察连接从获取到归还的真实耗时:
- 在
logback-spring.xml或application.properties中开启:
logging.level.com.zaxxer.hikari=DEBUG - 重启后触发问题接口,搜索日志中的
HikariPool-1 - Connection opened和对应HikariPool-1 - Connection returned to pool时间戳 - 若两者间隔 ≈ 整个方法执行时间(比如方法跑了 8 秒,连接也持有了 8 秒),基本坐实问题根源在事务方法内部逻辑
修复思路:拆分事务边界 + 异步解耦
核心原则:事务只包裹真正需要 ACID 保证的数据库操作,其他耗时动作一律剥离。
- 把耗时操作移出
@Transactional方法,改为方法内先 commit 数据,再调用非事务方法处理后续逻辑 - 对必须“事务后触发”的场景(如下单成功发短信),改用事件驱动:
— 使用@TransactionalEventListener(基于 Spring 事件,事务提交后自动触发)
— 或手动发消息到 MQ(如 RabbitMQ/Kafka),由消费者异步执行 - 若必须同步等待外部结果(如支付回调核验),考虑设置合理超时 + 降级策略,避免无限阻塞
- 禁用“伪事务”:避免在工具类、Service 工具方法上盲目加
@Transactional,尤其当它只是调用另一个已事务化的方法时

















