go中使用saga
如果说 TCC 是分布式事务中的“强一致性”代表,那么 Saga 模式就是“最终一致性”和“长事务”的王者。
Saga 模式最早在 1987 年由普林斯顿大学提出,它的核心思想非常朴素:把一个长事务拆分成一系列的本地短事务。如果所有步骤都成功,事务就完成;如果某一步失败,就按逆序执行补偿操作,把之前的数据“修补”回去。
它特别适合那些流程长、涉及服务多、且允许短暂中间状态的业务场景(比如电商下单、物流履约、保险理赔)。
🧩 核心概念:拆解与补偿
Saga 将一个全局事务拆解为一系列有序的本地事务(Local Transaction, $T_i$):
$$T_1, T_2, ..., T_n$$
每个本地事务 $T_i$ 都有一个对应的补偿事务(Compensating Transaction, $C_i$),用于撤销 $T_i$ 造成的影响。
执行流程
- 正常流程:依次执行 $T_1 \rightarrow T_2 \rightarrow ... \rightarrow T_n$。每个 $T_i$ 执行完立即提交(释放数据库锁),不等待后续步骤。
- 异常流程:假设执行到 $T_k$ 时失败了。
- 系统会触发向后恢复(Backward Recovery)。
- 按逆序执行补偿:$C_{k-1} \rightarrow ... \rightarrow C_2 \rightarrow C_1$。
- 最终让数据回到事务开始前的状态。
⚙️ 两种实现模式:编排 vs 协同
Saga 模式在工程落地时,主要有两种流派,你需要根据业务复杂度来选择:
1. 编排式 (Choreography) —— 去中心化
- 原理:没有中央指挥。每个服务做完自己的事后,发布一个事件(如“订单已创建”),下一个服务监听这个事件并执行自己的操作。
- 优点:服务之间解耦,没有单点故障风险。
- 缺点:流程逻辑分散在各个服务里,一旦流程变长(超过 3-4 步),依赖关系会变得像“蜘蛛网”一样复杂,难以调试和维护。
- 适用:简单的、线性的业务流程。
2. 协同式 (Orchestration) —— 中心化 (推荐)
- 原理:引入一个协调器(Orchestrator)。协调器像“指挥官”一样,定义好流程,依次调用各个服务。如果某一步失败,协调器负责按顺序调用补偿服务。
- 优点:流程逻辑集中管理,清晰可见,易于监控和排查问题。
- 缺点:协调器可能成为单点(需做高可用),且对业务有一定侵入。
- 适用:复杂的、多分支的、长流程的业务(如电商下单全流程)。
📊 Saga 与 TCC 的对比
这是面试和架构选型中最常被问到的问题,我为你整理了一个对比表:
| 维度 | TCC (Try-Confirm-Cancel) | Saga 模式 |
|---|---|---|
| 一致性 | 最终一致性(较强) | 最终一致性(较弱,有中间状态) |
| 性能 | 高(无长锁),但有资源预留开销 | 极高(无锁,资源利用率高) |
| 业务侵入 | 极高(需写 Try/Confirm/Cancel 三套代码) | 中等(只需写正向逻辑 + 补偿逻辑) |
| 隔离性 | 较好(资源预留阶段隔离) | 差(事务未提交前,数据修改对其他事务可见) |
| 适用场景 | 核心资金交易、高并发、短流程 | 长流程、跨部门/跨公司调用、非核心资金流 |
🛡️ 核心挑战:如何设计补偿?
Saga 的灵魂在于补偿机制。设计补偿时,你必须注意以下三个“坑”:
1. 补偿的语义
有些操作是不可逆的。
- 例子:你发短信通知用户“支付成功”。这个动作没法“撤回”。
- 解决:补偿操作不一定是“撤销”,也可以是“修正”。比如发送一条新短信说“支付异常,正在退款”。
2. 幂等性 (Idempotency)
和 TCC 一样,Saga 的正向操作和补偿操作都必须是幂等的。
- 场景:网络抖动导致协调器重试调用 $T_2$,或者重试调用 $C_1$。
- 要求:无论调用多少次,结果必须一致,不能重复扣款或重复恢复库存。
3. 空补偿与悬挂
- 空补偿:Try 没执行,Cancel 却先到了(网络延迟)。Cancel 需要识别这种情况,直接返回成功,不要报错。
- 悬挂:Cancel 先执行了(记录了回滚状态),Try 请求才迟到到达。Try 必须检查是否有回滚记录,如果有,拒绝执行。
💻 代码思路示例 (协同式)
在 Go 语言中,通常使用状态机或专门的框架(如 dtm)来实现。如果不使用框架,伪代码逻辑如下:
// 协调器逻辑
func (o *OrderOrchestrator) CreateOrder(orderID string) error {
// 1. 开启 Saga 事务记录(记录状态为 "STARTED")
// 2. 执行正向流程
if err := o.orderService.Create(orderID); err != nil {
return err
}
if err := o.inventoryService.Reduce(orderID); err != nil {
// 3. 失败!触发补偿
o.compensate(orderID, "INVENTORY_FAILED")
return err
}
if err := o.paymentService.Pay(orderID); err != nil {
// 4. 失败!触发补偿
o.compensate(orderID, "PAYMENT_FAILED")
return err
}
// 5. 全部成功
return nil
}
// 补偿逻辑
func (o *OrderOrchestrator) compensate(orderID, failStage string) {
// 根据失败阶段,逆序执行补偿
switch failStage {
case "PAYMENT_FAILED":
// 支付失败,需要恢复库存
o.inventoryService.Restore(orderID)
// 然后继续执行下面的 order cancel
fallthrough
case "INVENTORY_FAILED":
// 库存失败,取消订单
o.orderService.Cancel(orderID)
}
}
📌 总结建议
- 如果你的业务是“短平快”的核心交易(如秒杀、转账),且你能控制所有服务的代码,TCC 是更好的选择(一致性更强)。
- 如果你的业务是“长流程”(如:下单 -> 扣库存 -> 支付 -> 通知物流 -> 发送积分),或者涉及第三方系统(你无法修改对方代码让它支持 TCC),Saga 是唯一的出路。

浙公网安备 33010602011771号