DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Spring REQUIRES_NEW 并发等待连接,外层事务仍占着连接

外层事务保留连接,独立内事务另申请连接;结合实际并发、嵌套深度与数据库容量排查等待,不把文档更新当运行时修复。

线程已经进入外层事务,调用 REQUIRES_NEW 方法时却一直等连接:连接池 active 到达上限、idle 为零,业务日志只停留在外层方法。若每个线程都持有外层事务的连接,又在等内层的新连接,需要核对并发事务的资源预算。

这里讨论使用同一个 JDBC 数据源、事务确实经过 Spring 代理生效的场景。REQUIRES_NEW 会启动独立的物理事务;外层事务挂起后,它的连接仍要保留,以便内层结束后恢复。挂起事务并不会把那条连接还给池。

为什么十个线程能等住十条连接

假设连接池最多十条连接,有十个并发线程分别在外层事务中取得一条。它们随后都调用同一数据源上的 REQUIRES_NEW,就需要申请额外连接。此时池中没有空闲连接,外层又必须等内层返回才有机会结束,等待便形成了循环。

实际系统还可能有查询、后台任务和其他事务同时使用这个池,因此不能仅按 Web 工作线程数估算。先在同一时间段记录连接池的 active、idle、pending 或等待指标,再对照线程栈中的获取连接位置、外内事务入口和使用的数据源。只有 active 高,尚不足以判断连接泄漏。

也要先确认内层事务真的独立启动。通过 this 调用同类中的注解方法,可能没有经过代理;这一分支应另查事务代理调用路径,不能把未生效的注解当成第二条连接的证据。

增加连接之前,把并发与嵌套深度算清

Spring 文档对这种场景提示:连接池容量应至少比并发线程数多一。对于单层独立内事务,这为至少一个内事务取得连接、完成并归还连接留下了空间。它不是所有应用都适用的固定容量公式,也不是吞吐保证。

如果业务中还有多层 REQUIRES_NEW、其他数据源使用者或多实例部署,应一起计算峰值需求和数据库允许的总连接数。给每个实例增加池容量,可能把等待转移到数据库。内层等待外层持有的数据库锁,也不会因为连接多了一条就消失。

可以在隔离环境限制并发,观察等待是否随实际外层事务数变化,再确定池容量和入口并发控制。不要直接在生产环境用大量并发请求制造饱和。

连接获取超时同样应有明确设置。例如 HikariCP 的 connectionTimeout、Druid 的 maxWait 控制等待连接的时限,具体单位和允许值按使用版本核对。超时让失败能够返回,并没有补足资源;业务仍需正确处理失败、回滚和重试,避免每次重试再次占满池。

调整事务边界时保留原来的提交要求

先检查外层事务是否在不需要持有连接的工作期间仍占着资源。网络调用、长时间计算或等待其他任务,能否移出事务,需要根据一致性要求判断。缩短事务之后,应验证外层失败时哪些数据仍应回滚。

改传播方式之前也要写清语义:REQUIRED 通常参与外层事务,不能提供相同的独立提交;NESTED 通常依靠 JDBC 保存点,仍在同一物理事务中。它是否可用,还取决于事务管理器及资源支持。

异步执行或提交后再处理会改变执行时间、异常反馈和失败恢复方式。若业务要求内层结果在外层继续之前就已经独立提交,不能只为减少连接而替换成后台任务。确实改为提交后工作时,应另行安排失败记录、幂等与补偿。

这项连接保留行为不能简单归为旧版 Spring 的运行时缺陷。Framework 6.0.12 相关变更补充了文档警示;把依赖升级到含这段说明的版本,不会取消 REQUIRES_NEW 对独立资源的需求。

修改后,在受控并发下核对连接等待、获取连接超时、外内事务提交与回滚,以及实际数据库锁等待。保留原池配置和事务边界,回退配置不能撤销此前已经独立提交的内层事务。