数据一致性全靠它!Spring 事务传播行为没搞懂=大坑

原文来自于:zha-ge.cn/java/120

数据一致性全靠它!Spring 事务传播行为没搞懂=大坑

昨天和同事聊到一个订单系统的故事,我终于明白为什么"传播行为"这件小事会把数据搞得像过山车。表面上看,事务只是为了让数据要么全成功要么全失败,但在 Spring 里,它还会把我们救命的"边界"给挤来挤去。有时候你以为自己把两件事打包在同一个事务里,结果还没写完就崩了,数据就开始分崩离析。于是我把自己踩过的坑整理成这篇小记,愿意当你们的"救火绳"。

在一个典型的微服务场景里,往往会有一组相关的操作需要原子性:下单、扣库存、扣钱、发消息。这些步骤如果放在不同的事务里,哪怕只有一个环节失败,整条链路就会不一致。最常见的坑,是把库存扣减放在一个"独立事务"里(比如 Propagation.REQUIRES_NEW),而把下单和支付放在外层同一个事务里。结果就是:下单回滚了,库存却已经提交了,账也可能不一致。嗯,这就是传说中的"数据对不上号"的大坑。

踩坑瞬间

  • 场景要点:父事务包含多个子调用,子调用中有一个用到了 REQUIRES_NEW 的独立事务。父事务抛出异常后,子事务已经提交,导致最终数据不一致。

  • 关键代码节选(简化版,聚焦点在传播行为):

java 复制代码
@Service
public class OrderService {
  @Autowired private InventoryService inventoryService;
  @Autowired private PaymentService paymentService;

  @Transactional
  public void placeOrder(OrderDTO o) {
     inventoryService.reserve(o.getItemId(), o.getQty()); // 子调用在独立事务中执行
     paymentService.charge(o.getAmount()); // 仍在父事务内
     // ... 业务逻辑
     if (someError) throw new RuntimeException(); // 回滚父事务
  }
}

@Service
public class InventoryService {
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void reserve(Long itemId, int qty) {
     // 扣减库存,然后提交独立事务
  }
}

这段看起来像是"让两段逻辑都走完"的设计,实际效果却是:如果某处后续异常导致父事务回滚,库存的扣减已经在自己的独立事务里提交,永远不会被回滚。数据就成了两份不一致的账。

经验启示

  • 了解回滚边界:传播行为不是"好用就行",它其实在告诉你哪部分应该随父事务一起回滚,哪部分可以独立提交。明确哪些操作必须原子性地一起成功/失败。

  • 避免对相关业务使用 REQUIRES_NEW:如果你的下单、库存、支付是一个完整的业务流程,尽量让它们在同一个事务内完成,避免中间出现独立提交导致的最终不一致。

  • 需要独立提交时,也要有补偿机制:把"独立事务"放在真正独立的业务边界,如异步消息、外部系统调用后再做补偿,而不是直接让它们嵌在同一个调用链里随父事务的生死起伏。

  • 配置回滚规则要清晰:默认情况下运行时会回滚 RuntimeException,但如果你有自定义的异常类型,记得通过 rollbackFor 指定哪些异常会触发回滚,以免误判。

  • 小结:在设计事务边界时,先画好"数据一致性 needs"图,再决定传播行为。否则你写再多注解,也难免遇到那种在开发时"看起来对"的方案,上线就变成"大坑"。

写在最后

如果你现在还在犹豫传播行为到底怎么选,给自己一个简单的口径:要对数据强一致的部分,用同一个事务把相关调用绑死;要对外部系统或长时间任务进行解耦,使用明确的独立边界并配好补偿逻辑。别让一个小小的传播选择,把后续几十行代码的正确性全都打碎。愿你我都能在复杂的分布式场景里,靠这点"边界感"活成稳稳的老程序员。

相关推荐
q***064720 小时前
SpringSecurity相关jar包的介绍
android·前端·后端
低保和光头哪个先来20 小时前
场景2:Vue Router 中 query 与 params 的区别
前端·javascript·vue.js·前端框架
q***952221 小时前
SpringMVC 请求参数接收
前端·javascript·算法
陈橘又青21 小时前
100% AI 写的开源项目三周多已获得 800 star 了
人工智能·后端·ai·restful·数据
2501_9411486121 小时前
C++实时数据处理实战:多线程与异步IO结合高性能代码解析
java·后端·struts
爱学测试的雨果1 天前
收藏!软件测试面试题
开发语言·面试·职场和发展
文涛是个小白呀1 天前
Java集合大调研
java·学习·链表·面试
sen_shan1 天前
《Vue项目开发实战》第八章:组件封装--vxeGrid
前端·javascript·vue.js
五点六六六1 天前
双非同学校招笔记——离开字节入职小📕
前端·面试·程序员
IT_陈寒1 天前
Redis实战:5个高频应用场景下的性能优化技巧,让你的QPS提升50%
前端·人工智能·后端