mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1273 字
3 分钟
Saga 模式:长事务的编排方案
2025-02-28

某旅行平台预订流程:用户需要同时预订机票、酒店和租车。三个服务分别调用,如果机票预订成功但酒店满房,需要取消机票并通知用户。整个过程可能持续数秒甚至数分钟,2PC 的长时间锁阻塞不可接受,TCC 的资源冻结在跨企业场景下也不现实。Saga 模式通过正向操作与补偿操作配对,成为长事务场景的最佳选择。

一、Saga 的核心思想#

Saga 将长事务拆分为多个本地事务,每个本地事务都有对应的补偿操作:

  • 正向操作:执行业务逻辑
  • 补偿操作:撤销正向操作的影响

如果某个步骤失败,按反向顺序依次执行已完成步骤的补偿操作。

正向流程:T1 → T2 → T3 → T4(成功)
失败回滚:T1 → T2 → T3 失败 → C3 → C2 → C1(补偿)
Info

Saga 的补偿操作不是”回滚”,而是”语义上的撤销”。例如,扣款的补偿不是恢复余额,而是增加一笔等额的退款记录。两者的业务含义不同。

二、两种编排方式#

2.1 编排式(Orchestration)#

由一个中央协调器(Orchestrator)统一控制流程:

sequenceDiagram participant O as 协调器 participant F as 机票服务 participant H as 酒店服务 participant C as 租车服务 O->>F: 预订机票 F-->>O: 成功 O->>H: 预订酒店 H-->>O: 失败 O->>F: 取消机票(补偿)

优点:

  • 流程集中管理,逻辑清晰
  • 容易监控和调试
  • 适合复杂流程

缺点:

  • 协调器是单点,需要高可用保障
  • 协调器与所有服务耦合

2.2 协调式(Choreography)#

各服务通过事件驱动,自主决定下一步操作:

sequenceDiagram participant F as 机票服务 participant H as 酒店服务 participant C as 租车服务 participant E as 事件总线 F->>E: 机票预订成功事件 E->>H: 触发酒店预订 H->>E: 酒店预订失败事件 E->>F: 触发机票取消

优点:

  • 去中心化,无单点故障
  • 服务间松耦合
  • 适合简单流程

缺点:

  • 流程分散,难以整体理解
  • 调试困难,需要分布式追踪
  • 循环依赖风险

2.3 两种方式对比#

维度编排式协调式
流程可见性
单点风险
耦合度
调试难度
适用场景复杂流程简单流程

三、补偿操作设计#

3.1 补偿的语义#

补偿操作不是物理回滚,而是语义上的撤销:

正向操作补偿操作说明
扣款 100 元退款 100 元不是恢复余额,而是新增退款记录
创建订单取消订单订单状态变为”已取消”,而非删除
扣减库存恢复库存增加可用库存
发送邮件发送通知邮件无法撤回已发邮件,发送补充说明

3.2 补偿的幂等性#

补偿操作可能被重复调用,必须保证幂等:

public void cancelOrder(String txnId, String orderId) {
// 幂等性检查
if (compensationLog.exists(txnId, "CANCEL_ORDER")) {
return; // 已补偿,直接返回
}
Order order = orderMapper.selectById(orderId);
if (order.getStatus() == OrderStatus.CANCELLED) {
return; // 已取消,直接返回
}
order.setStatus(OrderStatus.CANCELLED);
orderMapper.updateById(order);
compensationLog.record(txnId, "CANCEL_ORDER");
}

3.3 补偿的顺序#

补偿必须按正向操作的反序执行:

正向:T1 → T2 → T3
补偿:C3 → C2 → C1

如果 T3 失败,先补偿 T2,再补偿 T1。这是因为后续操作可能依赖前序操作的结果。

四、Saga 的隔离性问题#

Saga 不提供全局隔离性,可能出现以下问题:

4.1 脏读#

事务 T1 执行后,T2 读取了 T1 的中间结果,但 T1 最终被补偿。

T1: 扣减库存(库存从 10 变为 9)
T2: 其他事务读取库存为 9
T1 补偿: 恢复库存(库存从 9 变为 10)
T2 基于过期数据做了决策

4.2 不可重复读#

同一事务内两次读取同一数据,结果不同。

4.3 幻读#

其他事务在 Saga 执行期间插入了新数据。

4.4 隔离性保障策略#

策略原理代价
语义锁在业务数据上标记”处理中”状态增加业务复杂度
交换律设计可交换的操作限制业务模型
悲观视图重排 Saga 步骤避免脏读可能增加延迟
版本控制使用乐观锁检测冲突冲突时需重试

五、Seata Saga 实践#

Seata 提供了基于状态机的 Saga 实现。

5.1 状态机定义#

{
"Name": "travelBooking",
"Comment": "旅行预订 Saga",
"StartState": "BookFlight",
"States": {
"BookFlight": {
"Type": "ServiceTask",
"ServiceName": "flightService",
"ServiceMethod": "book",
"CompensateState": "CancelFlight",
"Next": "BookHotel",
"Input": ["$.flightInfo"],
"Output": {"flightBookingId": "$.bookingId"}
},
"BookHotel": {
"Type": "ServiceTask",
"ServiceName": "hotelService",
"ServiceMethod": "book",
"CompensateState": "CancelHotel",
"Next": "BookCar",
"Input": ["$.hotelInfo"],
"Output": {"hotelBookingId": "$.bookingId"}
},
"BookCar": {
"Type": "ServiceTask",
"ServiceName": "carService",
"ServiceMethod": "book",
"CompensateState": "CancelCar",
"Next": "Succeed",
"Input": ["$.carInfo"]
},
"CancelFlight": {
"Type": "CompensateSubMachine",
"ServiceName": "flightService",
"ServiceMethod": "cancel"
},
"CancelHotel": {
"Type": "CompensateSubMachine",
"ServiceName": "hotelService",
"ServiceMethod": "cancel"
},
"CancelCar": {
"Type": "CompensateSubMachine",
"ServiceName": "carService",
"ServiceMethod": "cancel"
},
"Succeed": {
"Type": "Succeed"
}
}
}

5.2 状态机执行#

// 启动 Saga 状态机
StateMachineEngine engine = new DbStateMachineEngine(dataSource);
StateMachineInstance instance = engine.start(
"travelBooking",
BusinessType.COMMON,
params
);
// 查询执行状态
StateMachineInstance status = engine.queryInstance(instance.getId());

六、Saga 适用场景#

6.1 适合使用 Saga 的场景#

  • 长事务(秒级到分钟级)
  • 跨企业/跨组织的业务流程
  • 参与者数量较多
  • 可以接受最终一致性
  • 补偿操作语义明确

6.2 不适合使用 Saga 的场景#

  • 对隔离性要求高(如金融核心交易)
  • 补偿操作难以定义(如发送邮件)
  • 参与者数量少且事务短(2PC 更简单)
  • 实时性要求极高

七、Saga 与其他方案的对比#

维度2PCTCCSaga
一致性强一致最终一致最终一致
隔离性
阻塞严重
事务时长
业务侵入
补偿设计不需要需要需要
适用场景短事务高并发长流程

Saga 模式通过正向操作与补偿操作配对,解决了长事务场景下的分布式一致性问题。虽然不提供全局隔离性,但在跨服务、跨企业的业务流程中,Saga 是最实用的分布式事务方案。选择编排式还是协调式,取决于业务流程的复杂度和团队的技术能力。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

Saga 模式:长事务的编排方案
https://blog.souloss.com/posts/distributed/distributed-saga-pattern/
作者
Souloss
发布于
2025-02-28
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时