news 2026/10/15 6:52:02

Saga模式入门与实战:编排与协同模式、补偿设计原则与Java生产级实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Saga模式入门与实战:编排与协同模式、补偿设计原则与Java生产级实现

Saga模式入门与实战:编排与协同模式、补偿设计原则与Java生产级实现

在分布式系统中,Saga模式是一种通过将长事务拆解为多个可补偿的本地事务来维护数据最终一致性的架构模式。本文从核心概念讲起,深入对比编排与协同两种实现方式,并给出完整的Java/Spring Boot实战示例与选型建议。


博客:https://blog.csdn.net/badao_liumang_qizhi


一、为什么需要Saga模式

1.1 分布式事务的困境

在单体应用中,事务管理非常简单——Spring的@Transactional就能搞定一切。但当我们把系统拆分为微服务后,每个服务拥有独立的数据库,传统的ACID事务不再适用。一个跨越订单服务、支付服务、库存服务的业务流程,如何保证“要么全成功、要么全失败”?

类比:Saga就像跨国旅行的行李转运。你从北京出发,经上海转机到巴黎。每段航程(本地事务)独立完成并立即确认。但如果上海到巴黎的航班取消了(某一步失败),你不能“撤销”已经飞完的北京到上海段——你只能让航空公司把行李从上海运回北京(补偿操作)。这就是Saga的核心理念:不追求实时一致性,而是通过补偿操作实现最终一致性。

1.2 Saga的起源与核心思想

Saga这个概念来源于三十多年前的一篇数据库论文,一个Saga事务是一个有多个短时事务组成的长时的事务。在分布式事务场景下,我们把一个Saga分布式事务看作是一个由多个本地事务组成的事务,每个本地事务都有一个与之对应的补偿事务。

Saga通过一系列有序的本地事务和相应的补偿事务来保证最终一致性,每个本地事务完成后立即提交,如果发生错误,则按照顺序执行相应的补偿事务以回滚之前的操作。

关键要点:Saga不像2PC那样“先锁定再提交”,而是“先提交再说”——先用着,如果失败再作补偿回去。这种设计避免了2PC的同步阻塞和单点故障问题,代价是牺牲了隔离性。

1.3 Saga vs TCC:核心区别

维度TCCSaga
一致性强一致(业务层)最终一致
开发成本高,每个操作要写Try/Confirm/Cancel中,每个操作写正向+补偿
侵入性非常强,每个业务操作需拆为三个阶段中,主要增加补偿逻辑
适用场景资金等对一致性要求极高的场景长流程、可补偿业务
隔离性通过资源预留保证不保证,需要额外的隔离性应对策略

选型建议:核心链路用TCC保证强一致性,外围流程用Saga提升性能。Saga的优势很明显——最终一致性足以满足大多数业务场景,对现有业务代码侵入小,主要是增加补偿逻辑。


二、Saga的两种实现模式

Saga的执行模式主要有两种:编排模式和协同模式。

2.1 编排模式(Orchestration)

由一个集中式的协调者(Orchestrator)管理和控制Saga的执行,负责触发每个本地事务并处理失败后的补偿事务。

┌─────────────────────────────┐ │ Saga 协调者 (Orchestrator) │ └──────────────┬──────────────┘ │ ┌──────────────────┼──────────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ 创建订单 │─────▶│ 扣减库存 │─────▶│ 扣款支付 │ │ (T1) │ │ (T2) │ │ (T3) │ └─────────┘ └─────────┘ └─────────┘ │ 失败 ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ 取消订单 │◀─────│ 恢复库存 │◀─────│ 退款 │ │ (C1) │ │ (C2) │ │ (C3) │ └─────────┘ └─────────┘ └─────────┘

优点:中央控制,协调者统一管理事务和补偿逻辑,简化了事务管理;清晰的事务流,事务和补偿步骤的执行顺序明确。

缺点:单点故障,协调者成为单点故障,需要保证其高可用性;复杂度集中,协调者需要处理所有事务和补偿逻辑。

适用场景:业务流程明确、步骤较少;事务失败后需要严格执行补偿逻辑;中央协调能够简化业务逻辑。

2.2 协同模式(Choreography)

每个服务自己决定何时开始下一个本地事务,通过事件驱动的方式进行协调。把Saga的决策和执行顺序逻辑分布在每一个参与方中,它们通过交换事件的方式来进行沟通。

┌─────────┐ 事件:订单已创建 ┌─────────┐ 事件:库存已扣减 ┌─────────┐ │ 订单服务 │─────────────────▶│ 库存服务 │─────────────────▶│ 支付服务 │ │ │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ ▲ │ │ 事件:支付失败 → 触发补偿流程 │ └───────────────────────────────────────────────────────────┘

优点:松耦合,服务之间不需要直接依赖;适合简单流程,对于参与方较少的场景更简洁。

缺点:紧耦合(服务之间通过事件隐式耦合)、分布式事实源、故障排查困难、测试环境复杂、维护成本高。协同式会存在循环依赖的问题,因此编排式被《微服务架构设计模式》作者所推荐。

2.3 两种模式的选择决策

选编排模式:流程步骤较多(>5步)、需要集中监控和追踪、团队希望清晰的事务流控制。

选协同模式:流程简单(2-3步)、服务之间已经通过事件驱动架构解耦、团队对分布式事件流有丰富经验。


三、补偿事务的设计原则

3.1 三个核心原则

Saga的事务性补偿是关键,补偿事务需要保证以下三点:

原则一:幂等性—— 补偿事务需要幂等,以确保多次执行的结果一致。在分布式事务执行过程中,原服务与补偿服务都需要保证幂等性。由于网络连接可能超时,可以设置重试策略,重试发生时要通过幂等控制避免业务数据重复更新。

类比:幂等性就像按电梯按钮。按一次电梯来,按五次电梯还是来一次——结果不会因为操作次数而改变。补偿操作必须设计为“按多少次都一样”。

原则二:允许空补偿—— 空补偿指的是原服务未执行,补偿服务已执行。针对该问题,在服务设计时,需要允许空补偿,即在没有找到要补偿的业务主键时,返回补偿成功,并将原业务主键记录下来,标记该业务流水已补偿成功。

原则三:防止悬挂—— 悬挂指的是补偿服务比原服务先执行。此时需要检查当前业务主键是否已经在空补偿记录下来的业务主键中存在,如果存在则要拒绝执行该笔服务,以免造成数据不一致。

3.2 隔离性问题与应对

Saga模式由于一阶段已经提交本地数据库事务,且没有进行预留动作,因此无法保证隔离性。在极端情况下,可能由于脏写无法完成回滚操作。

应对策略:在实践中,一般可以采取以下方法应对该问题:

  • “宁可长款,不可短款”原则:业务流程设计时,始终遵循这个原则。长款指的是客户少了钱机构多了钱,以机构信誉可以给客户退款;反之则是短款,少的钱可能追不回来了。所以在业务流程设计上,一定是先扣款。
  • 向前恢复能力:有些业务场景可以允许让业务最终成功,在回滚不了的情况下,可以继续重试完成后面的流程。状态机引擎除了提供“回滚”能力还需要提供“向前”恢复上下文继续执行的能力,让业务最终执行成功,达到最终一致性的目的。

3.3 补偿操作的重试

补偿操作本身也可能失败,需要重试。但重试就要保证幂等,不然会出问题。Saga补偿操作必须加幂等检查。建议为补偿操作配置独立的重试策略和死信队列,当补偿操作多次重试仍失败时,触发人工介入告警。


四、Java实战:Spring Boot实现Saga模式

4.1 依赖配置

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency></dependencies>

4.2 编排模式实现:Saga协调者

@ServicepublicclassOrderSagaOrchestrator{privatefinalOrderServiceorderService;privatefinalPaymentServicepaymentService;privatefinalInventoryServiceinventoryService;/** * 编排模式Saga执行 * 正向操作按顺序执行,失败时反向补偿 */publicSagaResultexecuteOrder(OrderRequestrequest){List<SagaStep>executedSteps=newArrayList<>();SagaContextcontext=newSagaContext(request);try{// Step 1: 创建订单orderService.createOrder(context);executedSteps.add(newSagaStep("createOrder",()->orderService.cancelOrder(context)));// Step 2: 扣减库存inventoryService.reserveStock(context);executedSteps.add(newSagaStep("reserveStock",()->inventoryService.releaseStock(context)));// Step 3: 扣款支付paymentService.charge(context);executedSteps.add(newSagaStep("charge",()->paymentService.refund(context)));returnSagaResult.success(context);}catch(Exceptionerror){// 反向补偿Collections.reverse(executedSteps);for(SagaStepstep:executedSteps){try{step.compensate();}catch(ExceptioncompError){// 补偿失败,记录到死信队列,人工介入deadLetterQueue.add(newCompensationFailure(step.getName(),context,compError));}}returnSagaResult.failure(error);}}}

4.3 协同模式实现:事件驱动

// OrderService.java - 发布事件@Service@TransactionalpublicclassOrderService{privatefinalKafkaTemplate<String,OrderEvent>kafkaTemplate;publicOrdercreateOrder(OrderRequestrequest){Orderorder=newOrder();order.setStatus(OrderStatus.PENDING);order=orderRepository.save(order);// 发布事件,触发下一个服务的库存扣减kafkaTemplate.send("order-created",newOrderEvent(order.getId(),request.getProductId(),request.getQuantity(),request.getAmount()));returnorder;}}// InventoryService.java - 监听事件@ServicepublicclassInventoryService{@KafkaListener(topics="order-created")publicvoidonOrderCreated(OrderEventevent){try{inventoryRepository.reserveStock(event.productId(),event.quantity());// 库存扣减成功,触发支付kafkaTemplate.send("stock-reserved",event);}catch(Exceptione){// 库存不足,触发补偿kafkaTemplate.send("order-cancelled",event);}}}

4.4 补偿操作配置

@ConfigurationpublicclassSagaCompensationConfig{@BeanpublicMap<String,CompensationAction>compensationMapping(){Map<String,CompensationAction>mapping=newHashMap<>();mapping.put("createOrder",ctx->orderService.cancelOrder(ctx.getOrderId()));mapping.put("reserveStock",ctx->inventoryService.releaseStock(ctx.getProductId(),ctx.getQuantity()));mapping.put("charge",ctx->paymentService.refund(ctx.getPaymentId()));returnmapping;}}

五、生产运维与最佳实践

5.1 故障处理

故障类型处理策略
正向操作超时重试(指数退避,最多3次),仍失败则触发补偿
补偿操作失败进入死信队列,发送告警,人工介入
协调者宕机基于状态机的检查点恢复,从最后成功步骤继续
网络分区基于幂等性重试,防止重复执行

5.2 监控指标

指标目标值说明
Saga成功率> 95%正向操作全部成功的比例
补偿成功率> 99%补偿操作成功回滚的比例
平均Saga耗时< 30秒端到端执行时间
死信队列积压= 0需要人工介入的补偿失败数
补偿触发率< 5%触发补偿的Saga占比

5.3 最佳实践清单

  1. 补偿操作必须幂等:使用事务ID去重,防止重复执行
  2. 允许空补偿:补偿操作找不到原业务记录时返回成功
  3. 防止悬挂:补偿先于正向执行时拒绝处理
  4. 记录完整审计日志:每次正向和补偿操作都记录到数据库
  5. 补偿操作独立重试:与正向操作分离的重试策略和死信队列
  6. “宁可长款,不可短款”:业务流程设计先扣款后加款
  7. 控制Saga步骤数:建议不超过5步,步骤越多补偿越复杂

六、总结

Saga模式是分布式事务中一种实用且低侵入的解决方案。它通过“拆分本地事务+补偿事务”的思路,实现了低侵入、高适配的最终一致性。

维度编排模式协同模式
控制方式中央协调者事件驱动
复杂度集中在协调者分散在各服务
适合步骤数多步骤(>5步)简单流程(2-3步)
可维护性高低
推荐度★★★★★★★★

推荐学习路径:

  1. 先理解Saga与TCC、2PC的本质区别
  2. 掌握编排模式(推荐首选)的实现方式
  3. 深入理解幂等、空补偿、悬挂三大设计原则
  4. 在Spring Boot中实践电商订单的Saga流程
  5. 学习Seata Saga、ServiceComb Pack等生产级框架

参考资源:

  • Apache ServiceComb Pack
  • Seata SAGA 模式
  • Saga模式解析分布式长事务原理与实现 - 阿里云
  • Saga 模式服务设计 - SOFAStack
  • How to Implement Saga Pattern in Spring Boot
  • 微服务架构中的Saga模式详解 - Baeldung中文网
  • 分布式事务最佳实践:TCC空回滚、Saga补偿风暴与选型建议 - 阿里云

博客: https://blog.csdn.net/badao_liumang_qizhi

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 6:51:53

PHP应用集成CNN海草识别:跨语言推理服务架构与工程实践

1. 为什么要把CNN海草识别塞进PHP应用里第一次听到这个需求的人&#xff0c;十有八九会皱眉头&#xff1a;PHP 不是写网页、连数据库、渲染模板的吗&#xff0c;怎么跟卷积神经网络扯上关系了&#xff1f;我当初接到这个活儿的时候也是同样的反应。但把场景摊开来看&#xff0c…

作者头像 李华
网站建设 2026/10/15 6:50:32

jQuery和Bootstrap到底有什么区别?一文讲透定位与分工

先说结论&#xff1a;jQuery 和 Bootstrap 压根不是一个赛道上的东西。但既然这么多人把这两个词放在一起比了&#xff0c;就说明这种混淆背后有普遍原因——不是大家水平差&#xff0c;而是这两个老伙计经常在同一批项目里出现&#xff0c;时间久了&#xff0c;很多人就默认它…

作者头像 李华
网站建设 2026/10/15 6:50:10

基于Whisper与python-docx的听力语料转写与答案文档生成实战

简介&#xff1a;这份文档面向西北工业大学修读核心听力课程的学生&#xff0c;以及需要借助雨课堂平台完成听力练习与复习的学习者&#xff0c;集中整理了课程各单元的参考答案与要点提示&#xff0c;帮助解决课后核对、考前梳理和薄弱环节定位的问题。资源包共1个docx文件&am…

作者头像 李华
网站建设 2026/10/15 6:50:01

Qt QSplitter全面解析:可拖拽分栏布局的实战技巧

QSplitter 是 Qt 里一个特别容易被忽略的组件&#xff0c;但这几年做桌面端工具&#xff0c;我几乎每个项目都离不开它。如果你需要在界面里提供"可拖拽分割区域"的能力——比如编辑器左右分栏、文件管理器树状目录加详情列表、参数面板的上下折叠——QSplitter 就是…

作者头像 李华
网站建设 2026/10/15 6:49:12

TensorFlow核心概念与实战:从计算图到模型训练全解析

1. 从“tensorflow”这个标题说起&#xff1a;它到底是什么&#xff0c;能解决什么问题如果你在技术社区里闲逛&#xff0c;看到有人只甩出一个词——“tensorflow”&#xff0c;然后底下跟了一堆讨论&#xff0c;别惊讶。这个词在圈子里就像“电钻”在装修队里一样&#xff0c…

作者头像 李华
网站建设 2026/10/15 6:48:56

上下文模式(Context-Mode)工程实践:设计、隔离与链路追踪

1. 从“上下文模式”说起&#xff1a;一个被低估的工程概念第一次看到“context-mode”这个词&#xff0c;很多人会以为是某个新出的框架或者库。其实不是。它更像是一种设计思路&#xff0c;一种在系统开发中处理“上下文”这件事的工程模式。我在过去几年里&#xff0c;参与过…

作者头像 李华