解析分布式事务解决方案 - Saga 模式
学习思考
Jan 21, 2024
type
Post
status
Published
date
Jan 21, 2024
slug
saga-pattern
summary
Saga 模式是在分布式事务场景下管理跨服务数据一致性的一种方式,简而言之,Saga 是一系列事务的序列,它更新每个服务并发布消息或事件来触发下一个事务步骤,如果某个步骤失败,Saga 将执行补偿事务,以抵消之前事务的更改。本文将会全面解析此模式,让你掌握如何实现一个 Saga 模式的框架来实现数据的最终一致性。
tags
Architecture
分布式
category
学习思考
icon
password
Property
Feb 22, 2024 05:21 AM
在如今这个数字化和全球化的社会中,交付促进社会参与者互联互通的软件系统变得越来越重要,也变得越来越复杂,这使得分布式系统已经成为现代技术环境的不可或缺的一部分,分布式系统自然而然带来了新的需求,如何保持数据一致性就是其中比较困难的一个,单机环境下传统的事务就能保证的 ACID 原则,新环境中已经不再适用,我们需要寻找新的解决方案。

背景和问题

当架构师和开发人员考虑事务时,他们通常会考虑单个原子工作单元:多个数据库操作要么一起提交,要么在发生错误时全部回滚,这种类型的原子事务通常称为 ACID 事务。1983年,Andreas Reuter 和 Theo Härder 创造了首字母缩略词 ACID(Atomicity/Consistency/Isolation/Durability),这四个属性是事务范式的主要保证,对数据库系统开发的许多方面产生了重要影响,我们来分析一下传统事务模型中 ACID 的意义
  • 原子性 Atomicity:它意味着事务必须提交或回滚单个工作单元中的所有操作,无论在事务中包含多少操作。换句话说,所有更新都被视为一个整体,因此所有更改要么被提交,要么作为一个单元回滚。例如,假设注册客户涉及将客户资料插入客户资料表中,将信用卡信息插入钱包表中,以及将与安全相关的信息插入安全表中。如果资料和信用卡信息插入成功,但安全信息插入失败,那么由于原子性,资料和信用卡的插入也会被回滚,从而保持数据间的同步。
  • 一致性 Consistency:它意味着在事务过程中,数据库永远不会处于不一致状态,或违反数据库中指定的任何完整性约束,只会从一个合法的状态进入下一个合法的状态。例如,在事务期间,如果不首先添加相应的汇总记录(例如订单),系统就无法添加明细记录(例如商品)。尽管某些数据库将此检查推迟到提交时间,但通常程序员不能在事务过程中违反一致性约束,例如外键约束。
  • 隔离性 Isolation:指事务间交互的程度。隔离性保护未提交的事务数据在业务请求过程中对其他事务不可见。例如,在事务过程中,当客户资料信息插入客户资料表里时,在整个事务被提交之前,在事务范围之外的其他服务都不能访问新插入的信息。
  • 持久性 Durability:意味着一旦事务提交成功,就可以保证所有数据更新都是永久性的,其他系统故障均不会导致数据丢失和错误。
传统事务模型已经很完善且被大多数数据库支持,但是在进入现在高度连接带来的复杂系统时代,传统的事务模型已经没办法在分布式架构中正常工作,比如流行的 “Database-per-microservice” 模式,其中每个服务都有自己独立的数据库,这样单一的 ACID 事务就在系统的全局层级失效,我个人比较推荐微服务架构中采用此模式,因为
  1. 领域数据被封装在服务内部,确保领域知识也尽可能保持良好的封装
  1. 数据模式可以独立演化而不会直接影响其他服务
  1. 每个数据存储可以独立扩展,不受单一数据库的限制
  1. 最后就是一个服务中的数据存储故障不会直接影响其他服务
但是这个模式也带来了跨服务数据一致性的问题,而这就是我们将要分析的 Saga 模型可以解决的

方案

BASE 理论

在化学中,酸性(acid)物质和碱性(base)物质是完全相反的,ACID 事务与 BASE 事务也一样。
BASE 理论的完整论述出自 eBay 工程师 Dan Pritchett 的文章 Base: An Acid Alternative,它描述了分布式事务的属性:基本可用性、软状态和最终一致性。
  • 基本可用性(BA - Basically Available):意味着在分布式事务中,所有服务或系统都有望参与分布式事务,虽然分布式系统中的异步通信可以帮助解耦服务,并解决与分布式事务参与者相关的可用性问题,但它会影响数据变成原子业务事务一致所需的时间,所以这里强调的是基本可用性
  • 软状态(S - Soft State):描述了分布式事务正在进行并且原子业务请求的状态尚未完成(或者在某些情况下甚至不知道)的情况,因为对于使用异步通信的工作流,分布式事务的正在进行状态或最终状态通常很难确定
  • 最终一致性(E - Eventually consistent):意味着给予足够的时间,分布式事务的所有部分都将成功完成,并且所有数据彼此同步。最终一致性模式的类型和处理错误的方式,决定了分布式事务中涉及的所有数据源需要多长时间才能变得一致
通常我们也把基于 BASE 理论的分布式事务叫做柔性事务,事务过程中可能会有模糊的状态,数据不一致的情况,但最终结果肯定是一致的和可预测的。

典型的 Saga 模式

进入本文的正题 Saga 模式,它是解决分布式系统中数据一致性的方案之一,有很多种形式和定义,我们这里只介绍典型的 Saga 模式。Saga 模式通过一系列本地事务间接提供分布式全局事务管理的能力,本地事务是指由 Saga 参与者执行的原子性的工作单元,并非是特指 ACID 事务。在 Saga 流程中,每个本地事务更新数据库并发布消息或事件,以触发 Saga 中的下一个本地事务,如果某个本地事务失败,Saga 将执行一系列补偿事务,撤销之前本地事务所做的更改,确保数据的最终一致性。
notion image
在 Saga 流程中我们可能会碰到如下的几个概念,这里先做出基本的解释
  • 补偿事务:用来抵消本地事务产生影响的工作单元,必须是幂等和可重试的,这两个原则确保我们可以管理事务而无需任何手动数据干预,让补偿过程在补偿事务中完成。
  • 可补偿事务:指可以通过执行与其效果相反的另一补偿事务来潜在地撤销其影响的事务。
  • 关键事务: Saga 中的一个决定继续或放弃的点,如果关键事务提交,则 Saga 流程会运行到完成,关键事务可以是既不可补偿又不可重试的事务,或者可以是Saga中的最后一个可补偿事务或第一个可重试事务,根据实际需求确定。

实现 Saga 模式

常见的实现 Saga 模式的方案有两种,一种是协同(Choreography),一种是编排( Orchestration),两者就我看来最直接的区别就是是否存在一个独立的协调者。下面我们来分析一下这两种常见的模式的一般步骤和优缺点

使用协同实现 Saga 模式

使用协同方案来实现 Saga 模式的时候,参与者之间通信模式可以是同步也可以是异步,但工作流是分散协作式。换句话说,这种模式不需要中央编排器,将工作流协调职责完全赋予领域服务,架构图如下所示:
notion image
该实现方式特点
在这个工作流中,服务收到请求就执行操作,然后发送新的请求给另一个服务,每个服务“拥有”自己的事务,因此架构师必须将工作流的错误处理设计到领域内部,自行处理补偿和通知其他服务进行补偿。
一般来说,由于没有集中编排,工作流复杂性和分散协作解决方案的复杂性成比例——工作流越复杂,分散协作越困难,而且没有编排器意味着每个领域服务必须管理大多数工作流状态和信息,参与者需要自行在没有集中控制点的情况下交换事件,因此,这种模式最适合简单的工作流。
从架构设计方面看,对于要求高吞吐量的解决方案,这种模式非常适合“即发即忘”风格的工作流,例如数据采集、批量交易等。然而,由于不存在中央编排器,领域服务必须自己处理错误和服务间协调,低耦合增加了这种模式的伸缩性,需要引入异步通信才能获得更好的可伸缩性,而且由于这种模式缺乏全局事务管理,架构师必须付出额外的努力来同步数据,当然这种松散协作通常不会引入单点故障,因为责任分散在 Saga 参与者之间,所以在可用性方面比较容易保证。

使用编排实现 Saga 模式

编排是一种协调 Saga 参与者的另外一种方式,此方式需要一个集中控制器或者叫编排器,编排器处理所有业务请求,并根据业务事件告诉参与者执行哪些操作,它能够存储和解释每个任务的状态,并通过补偿事务处理故障恢复。
notion image
该实现方式特点
在此方案的典型实现中,编排器负责编排请求、响应和处理错误,但编排器不负责管理事务,每个领域服务都负责管理自己的本地事务,见上图,编排器可以负责执行补偿事务,但是这也不意味着需要参与某个正在进行中的正常事务。
这是一种更具吸引力的实现,普遍用于微服务架构,使用编排器可以更轻松地管理工作流,如果采用同步通信,实现起来比异步通信业更加容易,最终一致性绕开了最难以处理的事务协调问题,尤其是错误和异常场景的处理。
在架构设计层面上来看,集中编排和同步通信增加了系统耦合度,同时它采用最容易实现的方案(集中编排、同步通信)和最宽松的事务限制(最终一致性)降低了系统实现的复杂度,响应性也相对来说会表现的更好,因为虽然服务调用是同步的,但是编排器只需要维护少量的、与正在进行的事务相关的、时间敏感度较低的状态,不会导致响应时间的增加,没有事务耦合意味着每个服务可以独立伸缩,所以在可伸缩性和弹性上来看也是具有良好的表现

综述

无论是协同还是编排,Saga 模式为我们处理分布式系统事务带来了巨大的便利,通过了解具体的实现模式,我们也可以自动动手去实现一个 Saga 框架,或者更好的使用现有的支持 Saga 模式的框架如 Seata
具体深入到实现层面 Saga 模式还有各种不同的实现风格,通信方式的同步和异步、是否有集中编排、是否是最终一致性,这三个方面的决策都会导致实现路径的不同,也会带来不同的架构设计取舍,通过考虑各种场景和需求,我们应该选择最适合我们的实现策略,来高效、可靠地处理分布式事务。
 
  • Architecture
  • 分布式
  • 详解 ClickHouse 中的 MergeTree 引擎工作原理-1
    重新实现 Specification 模式