Saga 设计模式通过跨多个服务协调事务来帮助维护分布式系统中的数据一致性。 Saga 是一系列本地事务,其中每个服务执行其操作,并通过事件或消息启动下一步。 如果序列中的步骤失败,则 saga 执行补偿事务以撤消已完成的步骤。 此方法有助于维护数据一致性。
事务 表示可以包含多个操作的工作单元。 在事务中,事件 是指影响实体的状态更改。 命令 封装执行操作或触发后续事件所需的所有信息。
事务必须遵循原子性、一致性、隔离性和持久性(ACID)的原则。
- 原子性: 所有作都成功或未成功。
- 一致性: 数据从一个有效状态转换为另一个有效状态。
- 隔离: 并发事务产生与顺序事务相同的结果。
- 持久性:即使发生故障, 更改在提交后仍会保留。
在单个服务中,事务遵循 ACID 原则,因为它们在单个数据库中运行。 但是,实现跨多个服务的 ACID 符合性可能更为复杂。
微服务体系结构通常将 专用数据库分配给每个微服务。 此方法提供以下几个优势:
- 每个服务封装其自己的数据。
- 每个服务都可以根据其特定需求使用最合适的数据库技术和架构。
- 可以独立缩放每个服务的数据库。
- 一个服务中的故障与其他服务隔离。
尽管有这些优势,但此体系结构使跨服务数据一致性复杂化。 传统的数据库保证(如 ACID)不适用于多个独立托管数据存储。 由于这些限制,依赖于进程间通信的体系结构或传统的事务模型(如两阶段提交协议)通常更适合 Saga 模式。
Saga 模式通过将事务分解为一系列 本地事务来管理事务。
显示传奇概述的
每个本地事务:
- 在单个服务中以原子方式完成其工作。
- 更新服务的数据库。
- 通过事件或消息启动下一个事务。
如果本地事务失败,则 saga 会执行一系列 补偿事务, 以扭转上述本地事务所做的更改。
补偿事务 可以撤消或补偿其他具有相反效果的事务。 如果传奇中的步骤失败,补偿事务将撤消可补偿事务所做的更改。
透视事务 用作传奇中不返回的点。 透视事务成功后,补偿事务不再相关。 必须为系统完成所有后续作才能实现一致的最终状态。 透视事务可以承担不同的角色,具体取决于传奇流程:
不可逆或不可编译的事务 无法撤消或重试。
可逆和提交的 之间的边界意味着透视事务可以是最后一个可撤消或可补偿的事务。 或者,它可以是传奇中的第一个可重试作。
可重试事务 遵循透视事务。 可重试事务是幂等的,有助于确保传奇可以达到其最终状态,即使发生临时故障也是如此。 他们帮助传奇最终实现了一致的状态。
这两种典型的传奇实现方法是 编舞 和 业务流程。 每个方法都有自己的一组挑战和技术来协调工作流。
在编舞方法中,服务交换事件时没有集中式控制器。 通过编舞,每个本地事务都会发布触发其他服务中的本地事务的域事件。
编舞的好处 | 编舞的缺点 |
---|---|
适用于具有少量服务的简单工作流,不需要协调逻辑。 | 添加新步骤时,工作流可能会令人困惑。 很难跟踪每个传奇参与者响应的命令。 |
协调不需要其他服务。 | 传奇参与者之间存在循环依赖关系的风险,因为它们必须相互使用命令。 |
不会引入单一故障点,因为责任分布在传奇参与者中。 | 集成测试很困难,因为所有服务都必须运行才能模拟事务。 |
在业务流程中,集中式控制器或 业务流程协调程序,处理所有事务,并告知参与者根据事件执行哪些作。 业务流程协调程序执行传奇请求、存储和解释每个任务的状态,并使用补偿事务处理故障恢复。
业务流程的优点 | 业务流程的缺点 |
---|---|
更适合复杂的工作流,或者添加新服务时。 | 其他设计复杂性需要协调逻辑的实现。 |
避免循环依赖项,因为业务流程协调程序管理流。 | 引入故障点,因为业务流程协调程序管理完整的工作流。 |
明确职责分离简化了服务逻辑。 |
在决定如何实现此模式时,请考虑以下几点:
设计思维转变: 采用 Saga 模式需要不同的思维模式。 它要求你专注于跨多个微服务的事务协调和数据一致性。
调试传奇的复杂性: 调试传奇可能比较复杂,特别是随着参与服务的数量的增长。
不可逆的本地数据库更改:无法回滚 数据,因为传奇参与者将更改提交到各自的数据库。
处理暂时性故障和幂等性: 当重复相同的作不会改变结果时,系统必须有效处理暂时性故障并确保幂等性。 有关详细信息,请参阅 幂等消息处理。
监视和跟踪传奇故事的需要: 监视和跟踪传奇故事的工作流是维护作监督的重要任务。
补偿事务的限制: 补偿事务可能并不总是成功,这可能会使系统处于不一致状态。
数据异常是跨多个服务运行传奇时可能发生的不一致现象。 由于每个服务都管理自己的数据,称为 参与者数据,因此服务之间没有内置隔离。 此设置可能会导致数据不一致或持续性问题,例如服务之间的部分应用更新或冲突。 典型问题包括:
丢失的更新: 当一个传奇修改数据而不考虑另一个传奇发生更改时,会导致覆盖或丢失更新。
脏读取: 当传奇或事务读取另一个传奇已修改的数据时,但修改未完成。
模糊或不可恢复的读取: 当传奇中的不同步骤读取不一致数据时,因为读取之间发生更新。
若要减少或防止这些异常,请考虑以下对策:
语义锁:当 saga 的可补偿事务使用信号灯指示更新正在进行时, 使用应用程序级锁。
通勤更新: 设计更新,以便可以按任意顺序应用更新,同时仍生成相同的结果。 此方法有助于减少传奇之间的冲突。
悲观视图: 重新排序传奇的序列,以便数据更新在可重试的事务中发生,以消除脏读取。 否则,一个传奇可以读取脏数据,或 未提交的更改,而另一个传奇同时执行可补偿事务来回滚其更新。
重新读取值: 确认数据在进行更新之前保持不变。 如果数据发生更改,请停止当前步骤,并根据需要重启传奇。
版本文件: 维护对记录执行的所有作的日志,并确保它们按正确的顺序执行,以防止冲突。
基于风险的值的并发: 根据潜在业务风险动态选择适当的并发机制。 例如,对低风险更新使用 sagas,将分布式事务用于高风险更新。
在以下情况下使用此模式:
- 你需要确保分布式系统中的数据一致性,而无需紧密耦合。
- 如果需要回滚或补偿序列中的某个作失败。
此模式在以下情况下可能不适用:
- 事务紧密耦合。
- 补偿事务发生在早期参与者中。
- 存在循环依赖关系。
实现此模式时,以下模式可能相关:
编舞模式 让系统的每个组件都参与有关业务事务工作流的决策过程,而不是依赖于中心控制点。
补偿事务模式 撤消由一系列步骤执行的工作,并在一个或多个步骤失败时最终定义一致的作。 实现复杂业务流程和工作流的云托管应用程序通常遵循此 最终一致性模型。
重试模式 允许应用程序在尝试通过透明重试失败的作连接到服务或网络资源时处理暂时性故障。 此模式可以提高应用程序的稳定性。
断路器模式 处理在连接到远程服务或资源时需要花费一定时间从中恢复的故障。 此模式可以提高应用程序的稳定性和复原能力。
运行状况终结点监视模式 在应用程序中实现功能检查,外部工具可以定期通过公开的终结点进行访问。 此模式可帮助你验证应用程序和服务是否正常运行。