数据一致性全靠它!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"图,再决定传播行为。否则你写再多注解,也难免遇到那种在开发时"看起来对"的方案,上线就变成"大坑"。

写在最后

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

相关推荐
不一样的少年_4 小时前
女朋友炸了:刚打开的网页怎么又没了?我反手甩出一键恢复按钮!
前端·javascript·浏览器
Asort4 小时前
JavaScript设计模式(十四)——命令模式:解耦请求发送者与接收者
前端·javascript·设计模式
三七互娱后端团队4 小时前
Serena语义检索在AI CodeReview中的应用
后端
Java水解4 小时前
Nginx平滑升级与location配置案例详解
后端·nginx
小茴香3534 小时前
Vue 脚手架(Vue CLI)
前端·javascript·vue.js
你的电影很有趣4 小时前
lesson72:Node.js 安全实战:Crypto-Js 4.2.0 与 Express 加密体系构建指南
javascript·安全·node.js
聪明的笨猪猪4 小时前
Java 面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
Stringzhua4 小时前
Vue的Axios介绍【9】
前端·javascript·vue.js