WEBKT

Saga模式:微服务分布式事务的轻量级编排之道

4 0 0 0

在微服务架构日益普及的今天,如何优雅地处理分布式事务一直是开发者们面临的严峻挑战。你可能也像许多人一样,面对传统的2PC(两阶段提交)和TCC(Try-Confirm-Cancel)模式感到纠结:2PC虽然提供了强一致性,但其“重量级”的实现和全局锁机制在分布式高并发场景下显得力不从心,性能瓶颈和单点故障风险让人望而却步;而TCC虽然通过业务补偿机制实现了最终一致性,但其复杂的Try、Confirm、Cancel三阶段逻辑,以及对业务侵入性强、补偿代码编写繁琐的特点,也让不少开发者感到“难以驾驭”。

那么,有没有一种相对轻量级、更适合业务流程编排、能在保证最终一致性的同时,不至于让系统设计变得过于笨重的方式呢?答案是肯定的,那就是 Saga模式

什么是Saga模式?

Saga模式是一种管理分布式事务的设计模式,它将一个分布式事务分解为一系列本地事务。每个本地事务都更新其自身数据库,并发布一个事件以触发下一个本地事务。如果某个本地事务失败,Saga会执行一系列补偿事务来撤销之前成功的本地事务,从而确保整个分布式事务的最终一致性。

Saga模式的核心思想是:通过一系列具有原子性的本地事务来完成一个高阶的业务操作,并且为每个本地事务设计一个逆向的补偿操作。 它放弃了ACID事务的隔离性,转而通过事件和补偿机制来实现最终一致性。

为什么选择Saga而非2PC或TCC?

  1. 2PC的局限性: 2PC依赖一个全局的事务协调器,它在所有参与者都同意提交后才进行提交。这会引入性能瓶颈(所有参与者必须保持锁定资源直到事务完成),单点故障(协调器挂掉可能导致事务悬挂),以及长时间阻塞(任何一个参与者失败都可能导致整个事务回滚,且锁定时间长)。在微服务场景下,服务数量众多,2PC的开销和风险巨大。
  2. TCC的复杂性: TCC模式将一个业务操作分为Try、Confirm和Cancel三个阶段。Try阶段尝试执行并预留资源,Confirm阶段确认执行,Cancel阶段取消预留资源。它的优点是能将业务逻辑下沉到各个服务,灵活性高。但缺点也很明显:侵入性强,需要为每个业务操作编写Try、Confirm、Cancel三个接口;实现复杂,补偿逻辑的正确性验证和幂等性保证是难点;耦合度高,业务逻辑和事务控制混杂。

Saga模式则提供了一种更为弹性的选择:

  • 轻量级: 无需全局锁,每个服务只负责自己的本地事务,提高了系统的并发性和可用性。
  • 高弹性: 即使部分服务失败,也能通过补偿机制回滚,避免整个系统崩溃。
  • 适合业务编排: 通过事件或中心协调器,业务流程的每一步都可以清晰地被定义和管理,非常适合长流程的业务操作。
  • 最终一致性: 满足大部分业务对数据一致性的需求,通常在秒级或分钟级内达到最终一致。

Saga模式的两种实现方式

Saga模式主要有两种实现方式:编排(Orchestration)协同(Choreography)

  1. 编排(Orchestration Saga):

    • 工作原理: 引入一个中心化的Saga编排器(Saga Orchestrator)。编排器负责定义和管理整个Saga事务的流程,它知道Saga中所有本地事务的顺序以及如何处理失败。当一个本地事务完成后,它会通知编排器,编排器再决定执行下一个本地事务,或者在失败时触发补偿事务。
    • 优点: 事务流程清晰,便于追踪和管理;服务间耦合度较低,服务本身不需要知道其他服务的具体实现。
    • 缺点: 编排器可能成为单点瓶颈或单点故障。
    • 适用场景: 事务流程较复杂、步骤较多,需要清晰控制流程的场景。
  2. 协同(Choreography Saga):

    • 工作原理: 没有中心化的编排器。每个服务在完成自己的本地事务后,会发布一个领域事件(Domain Event),其他相关服务订阅并监听这些事件。当接收到特定事件后,这些服务会执行自己的本地事务,并可能发布新的事件。如果发生失败,也通过发布补偿事件来通知其他服务执行补偿操作。
    • 优点: 高度去中心化,消除了单点瓶颈,增强了系统的健壮性和伸缩性。
    • 缺点: 事务流程分散在各个服务中,难以追踪和理解整个事务的执行状态;服务间隐式耦合较强,对事件的依赖性高。
    • 适用场景: 事务流程相对简单、步骤较少,或者追求高度解耦和弹性的场景。

实际场景案例:在线订单处理

假设我们有一个在线商城系统,用户下单后需要完成以下一系列操作:

  1. 创建订单 (Order Service)
  2. 扣减库存 (Inventory Service)
  3. 处理支付 (Payment Service)
  4. 更新订单状态 (Order Service)

这是一个典型的分布式事务场景。我们用编排Saga模式来演示如何处理:

Saga流程:

  1. 用户发起下单请求,请求到达网关,转发至 订单服务 (Order Service)

  2. 订单服务 首先在自己的数据库中创建订单记录,状态为“待处理”。然后,它向 Saga编排器 发送一个“创建订单成功”的事件,并传递订单信息。

  3. Saga编排器 接收到事件后,开始启动Saga。它首先向 库存服务 (Inventory Service) 发送一个“扣减库存”的命令(或消息),附带订单商品信息。

    • 库存服务 接收命令,执行本地事务扣减库存。
      • 如果库存扣减成功: 库存服务向Saga编排器发送“库存扣减成功”事件。
      • 如果库存扣减失败(如库存不足): 库存服务向Saga编排器发送“库存扣减失败”事件。
        • Saga编排器处理失败: 接收到“库存扣减失败”事件后,编排器会启动补偿机制。它会向 订单服务 发送“取消订单”命令。订单服务将订单状态更新为“已取消”,并可能通知用户。至此,Saga结束。
  4. Saga编排器 接收到“库存扣减成功”事件后,继续下一步。它向 支付服务 (Payment Service) 发送一个“处理支付”的命令,附带订单金额和用户信息。

    • 支付服务 接收命令,执行本地事务处理支付。
      • 如果支付成功: 支付服务向Saga编排器发送“支付成功”事件。
      • 如果支付失败: 支付服务向Saga编排器发送“支付失败”事件。
        • Saga编排器处理失败: 接收到“支付失败”事件后,编排器同样启动补偿机制。它会先向 库存服务 发送“恢复库存”的补偿命令(撤销之前的扣减),然后向 订单服务 发送“取消订单”命令。至此,Saga结束。
  5. Saga编排器 接收到“支付成功”事件后,继续最后一步。它向 订单服务 (Order Service) 发送一个“更新订单状态”的命令,将订单状态更新为“已完成”。

  6. 订单服务 接收命令,更新订单状态。向Saga编排器发送“订单完成”事件。

  7. Saga编排器 接收到“订单完成”事件,整个Saga事务成功完成。

补偿事务(示例):

  • 订单服务:
    • 正向操作:createOrder(orderInfo) -> 创建订单,状态为“待处理”。
    • 补偿操作:cancelOrder(orderId) -> 将订单状态更新为“已取消”,可能释放占用的资源。
  • 库存服务:
    • 正向操作:deductInventory(productId, quantity) -> 扣减指定商品的库存。
    • 补偿操作:restoreInventory(productId, quantity) -> 恢复指定商品的库存(核心是幂等性,避免重复恢复)。
  • 支付服务:
    • 正向操作:processPayment(orderId, amount) -> 发起支付请求。
    • 补偿操作:refundPayment(orderId, amount) -> 如果已扣款,则发起退款。

通过这种方式,即使在复杂的多服务交互中,我们也能通过Saga模式实现最终一致性,并且每个服务的本地事务都保持了其自治性。

Saga模式的关键考量

  1. 幂等性 (Idempotency): 所有的正向操作和补偿操作都必须是幂等的。这意味着无论这些操作被执行多少次,其结果都应该是一致的。例如,多次调用restoreInventory只应恢复一次库存。
  2. 隔离性缺失与补偿: Saga模式放弃了事务的ACID隔离性,允许脏读、幻读等情况发生。这就需要业务层面去处理这些隔离性不足带来的问题,例如:
    • 对于正在进行中的Saga事务,查询时可能显示的是中间状态的数据。
    • 长事务可能导致并发冲突,需要业务逻辑来检测和处理。
    • 一个正在执行的Saga,其数据可能被另一个Saga读取,这需要我们思考业务对最终一致性的容忍度。
  3. 失败重试与超时: Saga编排器或各个服务需要有完善的失败重试机制,以处理网络瞬时故障或服务短暂不可用。同时,也要设定合理的超时机制,避免事务长时间挂起。
  4. 可观测性: 由于Saga事务跨越多个服务,分布式链路追踪(如使用OpenTracing/Zipkin)和集中式日志系统变得至关重要,以便快速定位问题和监控事务状态。
  5. 业务流程梳理: 在设计Saga之前,必须清晰地梳理业务流程,明确每个本地事务的边界、成功条件和对应的补偿逻辑。补偿逻辑的正确性和完整性是Saga模式成功的关键。

总结

Saga模式为微服务架构中的分布式事务提供了一个既不“笨重”也不“复杂”的优雅解决方案。它通过将复杂的全局事务分解为一系列本地事务,并利用补偿机制来保证最终一致性,特别适用于需要业务流程编排、对实时强一致性要求不高的场景。无论是选择中心化的编排Saga,还是去中心化的协同Saga,关键都在于深刻理解其原理,并结合实际业务场景进行权衡和设计。掌握Saga模式,将让你在微服务的世界里,面对分布式事务挑战时更加游刃有余。

架构小凡 微服务分布式事务Saga模式

评论点评