惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
有赞技术团队
有赞技术团队
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Threat Research - Cisco Blogs
L
LangChain Blog
Simon Willison's Weblog
Simon Willison's Weblog
Project Zero
Project Zero
Latest news
Latest news
S
Schneier on Security
Cisco Talos Blog
Cisco Talos Blog
MyScale Blog
MyScale Blog
C
Check Point Blog
IT之家
IT之家
P
Palo Alto Networks Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
The Hacker News
The Hacker News
C
CXSECURITY Database RSS Feed - CXSecurity.com
G
Google Developers Blog
T
Tor Project blog
T
Threatpost
D
DataBreaches.Net
博客园 - 【当耐特】
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Troy Hunt's Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
P
Privacy & Cybersecurity Law Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
博客园_首页
S
Securelist
T
The Exploit Database - CXSecurity.com
Last Week in AI
Last Week in AI
量子位
U
Unit 42
Know Your Adversary
Know Your Adversary
Hugging Face - Blog
Hugging Face - Blog
S
Security Affairs
Google Online Security Blog
Google Online Security Blog
Hacker News: Ask HN
Hacker News: Ask HN
Webroot Blog
Webroot Blog
S
SegmentFault 最新的问题
Engineering at Meta
Engineering at Meta
N
News and Events Feed by Topic
P
Proofpoint News Feed
阮一峰的网络日志
阮一峰的网络日志

博客园 - 干炸小黄鱼

timex 处理时间戳 gorm-gen go 雪花算法 golang每日一库--协程池库ants golang每日一库--json解析库gjson python高级编程-asyncio python高级编程-condition python高级编程-event python装饰器-自动重试 EAP系统 go实现实现 SECS/GEM 协议 设备通信协议 SECS go项目使用Jenkins进行CICD go操作ES mongo db聚合查询 go如何使用mongodb Apache ShardingSphere paxos and raft (分布式一致性算法) go使用zookeeper分布式锁以及和redis差异 go使用 seata 示例 Alibaba 分布式事务 Seata go中使用TCC示例 分布式事务TCC 熔断器 Hystrix OR Sentinel k8s下部署consul and etcd Consul OR Etcd 【力扣hot100】双指针-盛水最多的容器 【力扣hot100】滑动窗口-最小覆盖子串 shell脚本合集 分布式id生成器 springboot通用CURD Python PB级检索系统架构设计 rancher 在三台机器搭建k8s集群 python ssh clinet 数据库排序Null值字段靠后/靠前 常规web项目 docker-compose 例子 手搓一个验证码 使用itertools 中的groupby 对字典数组进行分组后排序 使用开源库 geoip2 获取某ip的经纬度地理信息 python中 apscheduler.schedulers.blocking.BlockingScheduler 定时执行任务 简单的python web项目的docker-compose.yml 示例 python和sliver交互 golang sliver二次开发自定义命令(格式乱后面再调) pydantic做参数校验 基于rancher部署k8s 地理位置相关基础数据 flask migrate时报错 Can't locate revision identified by '3d80e4c025df'
go中使用saga
干炸小黄鱼 · 2026-04-21 · via 博客园 - 干炸小黄鱼

如果说 TCC 是分布式事务中的“强一致性”代表,那么 Saga 模式就是“最终一致性”和“长事务”的王者。

Saga 模式最早在 1987 年由普林斯顿大学提出,它的核心思想非常朴素:把一个长事务拆分成一系列的本地短事务。如果所有步骤都成功,事务就完成;如果某一步失败,就按逆序执行补偿操作,把之前的数据“修补”回去。

它特别适合那些流程长、涉及服务多、且允许短暂中间状态的业务场景(比如电商下单、物流履约、保险理赔)。


🧩 核心概念:拆解与补偿

Saga 将一个全局事务拆解为一系列有序的本地事务(Local Transaction, $T_i$):
$$T_1, T_2, ..., T_n$$

每个本地事务 $T_i$ 都有一个对应的补偿事务(Compensating Transaction, $C_i$),用于撤销 $T_i$ 造成的影响。

执行流程

  1. 正常流程:依次执行 $T_1 \rightarrow T_2 \rightarrow ... \rightarrow T_n$。每个 $T_i$ 执行完立即提交(释放数据库锁),不等待后续步骤。
  2. 异常流程:假设执行到 $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 是唯一的出路。