引言
在微服务架构中,每个服务拥有独立数据库,跨服务数据一致性成为核心挑战。传统2PC(两阶段提交)在分布式环境下因阻塞和单点故障问题难以适用。Saga模式通过本地事务序列 补偿操作,提供了最终一致性的优雅方案。
1. Saga核心概念
Saga是一系列本地事务T1,T2,...,Tn,每个Ti都有对应的补偿事务Ci。若T3失败,则逆序执行C2,C1已完成的事务进行回滚。补偿事务不是传统数据库rollback——它将数据恢复到Ti执行前的业务状态。
Saga的ACID降级:原子性通过补偿保证,隔离性弱(可能读到中间状态),持久性和一致性由本地事务保证。这意味着需要处理脏读、不可重复读和幻读等业务层面的隔离问题。
2. 编配(Choreography)实现
每个服务发布领域事件,下游服务监听并执行对应事务。事务链:OrderCreated→PaymentReserved→InventoryReserved→OrderConfirmed。失败路径:PaymentReserved→PaymentFailed→OrderCancelled。
优势:去中心化、松耦合,每个服务理解自身职责。劣势:事务流散落在各服务中难以追踪,增加服务可能引发"事件风暴"的蝴蝶效应。调试困难——需从日志中重建Saga路径。
EventBridge/Kafka是常用基础设施。关键设计:事件携带SagaID实现因果链追踪;幂等消费(依据SagaID去重);死信队列处理持续失败。
3. 编排(Orchestration)实现
Saga编排器(流程协调器)集中管理事务序列。Axon Framework的Saga、Camunda的流程引擎、Temporal/CloudMaker的工作流都是典型实现。发送命令:ReservePayment→ReserveInventory→ConfirmOrder。
优势:集中控制流易于监控,清晰的流程定义(BPMN/DSL),超时和重试策略统一配置。劣势:编排器成为业务逻辑热点可能成为瓶颈,引入单点耦合。
现代方案采用持久化工作流引擎(Temporal),将Saga状态和调用栈持久化到数据库,支持长时间运行(数天)的Saga,天然处理crash recovery。
4. 工程难题与解决方案
幂等性:每个操作必须SagaID 操作ID幂等执行。PayPal的经验:v1版本因幂等缺陷导致double charge,损失数百万美元。解决方案:唯一性token存储在DB,执行前检查。
悬挂问题:若T2先于T1完成(因事件乱序),则T2成为悬挂事务。解决方案:版本号校验(服务拒绝旧版本事件),或采用forward recovery而非backward recovery。
缺失隔离:Saga的中间状态对外部可见。解决方案:业务层面的"预扣减"状态(如订单状态为pending),或对关键操作增加语义锁。
5. Temporal工作流引擎实战
Temporal将Saga实现为持久化工作流:Activity函数执行本地事务,补偿在defer中注册。Saga.builder().addStep(reservePayment, compensatePayment).addStep(reserveInventory, compensateInventory).build()。引擎自动处理:超时重试、crash恢复、版本兼容、可观测性。
与Axon等传统框架对比:Temporal将工作流代码与基础设施解耦(不用关心消息队列),支持多语言SDK(Java/Go/Python/TypeScript),持久栈实现任意时长的Saga。
结语
Saga模式不仅是技术方案,更是一种分布式系统思维的体现:接受最终一致性,通过补偿操作实现"业务层面的回滚"。选择编配还是编排取决于团队规模和系统复杂度——多数中大型系统最终倾向编排以获得可管理性。

发表评论 取消回复