你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

Saga 分布式事务模式

Azure

通过跨多个服务协调一系列本地事务来维护分布式系统中的数据一致性。 每个服务执行其操作,并通过事件或消息触发下一步。 如果步骤失败,一系列补偿事务将撤消已完成步骤所做的更改。

上下文和问题

事务 表示可以包含多个操作的工作单元。 在事务中,事件 是指影响实体的状态更改。 命令 封装执行操作或触发后续事件所需的所有信息。

事务必须遵循原子性、一致性、隔离性和持久性(ACID)的原则。

  • 原子性: 所有操作要么全部成功,要么全部不成功。
  • 一致性: 数据从一个有效状态转换为另一个有效状态。
  • 隔离: 并发事务产生与顺序事务相同的结果。
  • 持久性:更改一旦提交,即使发生故障也会保留。

在单个服务中,事务遵循 ACID 原则,因为它们在单个数据库中运行。 但是,实现跨多个服务的 ACID 符合性可能更为复杂。

微服务体系结构中的挑战

微服务体系结构通常将 专用数据库分配给每个微服务。 此方法提供以下几个优势:

  • 每个服务封装其自己的数据。
  • 每个服务都可以根据其特定需求使用最合适的数据库技术和架构。
  • 各个服务的数据库都可以独立扩展。
  • 一个服务中的故障与其他服务隔离。

尽管有这些优势,但此体系结构使跨服务数据一致性复杂化。 传统的数据库保证(如 ACID)不适用于多个独立托管数据存储。 由于这些限制,依赖于进程间通信的体系结构或传统的事务模型(如两阶段提交协议)通常更适合 Saga 模式。

解决方案

Saga 模式通过将事务分解为一系列 本地事务来管理事务。

显示 Saga 概述的图示。

各个本地事务:

  1. 在单个服务中以原子方式完成其工作。
  2. 更新服务的数据库。
  3. 通过事件或消息启动下一个事务。

如果本地事务失败,Saga 会执行一系列 补偿事务,以撤销之前的本地事务所做的更改。

Saga 模式中的关键概念

  • 可补偿事务可以被撤销,或者由具有相反效果的其他事务进行补偿。 如果 Saga 中的某个步骤失败,补偿事务会撤销可补偿事务所做的更改。

  • 枢轴事务充当 Saga 中不可回退的点。 枢纽事务成功后,补偿事务将不再适用。 系统必须完成所有后续操作,才能达到一致的最终状态。 枢纽事务可以扮演不同的角色,具体取决于 Saga 的流程:

    • 不可逆或不可补偿的事务无法撤销或重试。

    • 可逆与已提交之间的边界意味着枢轴事务可以是最后一个可撤销的事务,或最后一个可补偿的事务。 或者,它也可以是 Saga 中的第一个可重试操作。

  • 可重试事务位于枢纽事务之后。 可重试事务是幂等的,有助于确保 Saga 即使在发生临时故障时也能达到最终状态。 它们有助于使 Saga 最终达到一致状态。

Saga 实现方法

典型的 Saga 实现方式有两种:编舞编排。 每个方法都有自己的一组挑战和技术来协调工作流。

编舞

在编舞方法中,服务交换事件时没有集中式控制器。 在编排模式下,每个本地事务都会发布领域事件,从而触发其他服务中的本地事务。

图表,该图使用编舞显示传奇。

编舞的好处 编舞的缺点
适用于具有少量服务的简单工作流,不需要协调逻辑。 添加新步骤时,工作流可能会令人困惑。 很难跟踪每个 Saga 参与者会响应哪些命令。
协调不需要其他服务。 Saga 参与者之间存在循环依赖的风险,因为它们必须消费彼此的命令。
不会引入单点故障,因为职责分散在各个 Saga 参与者之间。 集成测试很困难,因为所有服务都必须运行才能模拟事务。

编排

在编排中,集中式控制器(即 编排器)负责处理所有事务,并根据事件告知参与者执行哪些操作。 编排器执行 Saga 请求,存储并解析每个任务的状态,并通过补偿事务进行故障恢复。

显示使用编排的 Saga 的示意图。

编排的优势 编排的缺点
更适合复杂的工作流,或需要添加新服务时。 其他设计复杂性需要协调逻辑的实现。
由于协调程序管理流程,因此可避免循环依赖。 这会引入单点故障,因为编排器管理着整个工作流。
明确职责分离简化了服务逻辑。

问题和注意事项

在决定如何实现此模式时,请考虑以下几点:

  • 设计思维转变: 采用 Saga 模式需要不同的思维模式。 它要求你专注于跨多个微服务的事务协调和数据一致性。

  • 调试传奇的复杂性: 调试传奇可能比较复杂,特别是随着参与服务的数量的增长。

  • 不可逆的本地数据库更改: 由于 Saga 参与者已将更改提交到各自的数据库中,因此数据无法回滚。

  • 处理暂时性故障和幂等性: 当重复相同的作不会改变结果时,系统必须有效处理暂时性故障并确保幂等性。 有关详细信息,请参阅 幂等消息处理

  • 监控和跟踪 Saga 的必要性: 监控和跟踪 Saga 的执行流程是保持对运行情况进行监督的重要任务。

  • 补偿事务的限制: 补偿事务可能并不总是成功,这可能会使系统处于不一致状态。

传奇中潜在的数据异常

数据异常是 Saga 在跨多个服务运行时可能出现的不一致情况。 由于每个服务都管理自己的数据,称为 参与者数据,因此服务之间没有内置隔离。 此设置可能会导致数据不一致或持续性问题,例如服务之间的部分应用更新或冲突。 典型问题包括:

  • 丢失的更新: 当一个 Saga 在修改数据时未考虑另一个 Saga 所做的更改,就会导致更新被覆盖或丢失。

  • 脏读: 当一个 Saga 或事务读取了另一个 Saga 已修改但该修改尚未完成的数据时。

  • 模糊读取,或不可重复读: 由于两次读取之间发生了更新,Saga 中的不同步骤会读取到不一致的数据。

解决数据异常的策略

若要减少或防止这些异常,请考虑以下对策:

  • 语义锁:当 saga 的可补偿事务使用信号灯指示更新正在进行时, 使用应用程序级锁。

  • 可交换更新: 将更新设计为可按任意顺序应用,同时仍得到相同的结果。 这种方法有助于减少各个 Saga 之间的冲突。

  • 悲观看法: 重新安排 Saga 的执行顺序,使数据更新在可重试事务中进行,从而消除脏读。 否则,一个 Saga 可能会读取到脏数据,或 未提交的更改,而另一个 Saga 同时执行补偿事务,对其更新进行回滚。

  • 重新读取值: 确认数据在进行更新之前保持不变。 如果数据发生变化,请停止当前步骤,并视需要重新启动 Saga。

  • 版本文件: 记录对某条记录执行的所有操作,并确保这些操作按正确的顺序执行,以防止冲突。

  • 基于风险的值的并发: 根据潜在业务风险动态选择适当的并发机制。 例如,对低风险更新使用 Saga 模式,对高风险更新使用分布式事务。

何时使用此模式

在以下情况下使用此模式:

  • 你需要确保分布式系统中的数据一致性,而无需紧密耦合。
  • 如果该序列中的某个操作失败,你需要回滚或进行补偿。

此模式在以下情况下可能不适用:

  • 事务紧密耦合。
  • 补偿事务发生在较早的参与方中。
  • 存在循环依赖关系。

下一步

实现此模式时,以下模式可能相关:

  • 编舞模式 让系统的每个组件都参与有关业务事务工作流的决策过程,而不是依赖于中心控制点。

  • 补偿事务模式 撤销由一系列步骤已执行的工作,并在一个或多个步骤失败时最终定义出一个一致的操作。 实现复杂业务流程和工作流的云托管应用程序通常遵循此 最终一致性模型

  • 重试模式 使应用程序能够在尝试连接到服务或网络资源时,通过透明地重试失败的操作来处理暂时性故障。 此模式可以提高应用程序的稳定性。

  • 断路器模式用于处理在连接到远程服务或资源时发生的、恢复所需时间长短不一的故障。 此模式可以提高应用程序的稳定性和复原能力。

  • 运行状况终结点监视模式在应用程序中实现功能检查,外部工具可定期通过公开的终结点访问这些检查。 此模式可帮助你验证应用程序和服务是否正常运行。