分布式事务解决方案
- 一、什么是事务?
- 二、什么是分布式事务?
- 三、分布式事务的理论模型
- 3.1 X/Open 分布式事务模型
- 3.1.1 X/Open事务执行流程
- 3.1.2 XA 协议
- 3.2 两阶段提交协议
- 3.3 三阶段提交协议
- 四、分布式事务场景解决方案
- 4.1 TCC补偿方案
- 4.2 基于可靠性消息的最终一致性方案
- 4.3 最大努力通知
- 五、分布式事务框架(Seate)
- 5.1 TCC模式
- 5.2 XA模式
- 5.3 AT模式
- 5.4 Saga长事务模式
一、什么是事务?
事务,大家第一时间想到的肯定是数据库事务,即,作为单个逻辑工作单元执行的多个数据库操作,要么同时成功,要么同时失败,且必须满足 ACID 特性,如下:
- 原子性(Atomicity):事务作为一个整体单位被执行,要么全部成功,要么全部失败。
- 一致性(Consistency):事务完成后,所有数据必须保持一致。
- 隔离性(Isolation):多个事务的执行是相互隔离的(不可见的)。
- 持久性(Durability):事务一旦提交,其修改将永久保存在数据库中,及时系统故障或重启也不会丢失。
值得注意的是,在实际软件开发过程中,因根据具体应用场景和需求合理设计事务,避免事务过大或过长时间锁定资源(长事务),进行影响系统的并发性能。
二、什么是分布式事务?
数据库事务针对的是在同一个数据库中,多个表操作的情况。在现今微服务架构中,通过 DDD 进行业务服务的拆分及数据库的拆分,用户同一个操作,可能会调用多个服务进行业务处理,在这种场景中,就产生了分布式事务。
那什么是分布式事务?
分布式事务是指涉及多个独立系统、服务的事务操作,这些系统、服务可能分布在不同的物理节点上,通过 RPC 进行通信,连接不同的数据库,分布式事务的目的是确保多个参与者在以上分布式环境下的操作具有 ACID 特性,以确保数据的完整性和一致性。
三、分布式事务的理论模型
3.1 X/Open 分布式事务模型
X/Open DTP(X/Open Distributed Transaction Processing Reference Model)是 X/Open 组织(目前已经与 The Open Group 合并)定义的一套分布式事务的标准,这个标准提出了使用两阶段提交(2PC,Two Phase Commit)来保证分布式事务的 ACID 特性。
X/Open DTP 中包含三种角色:
- AP:Application,应用程序。
- RM:Resource Manager,资源管理器,一般指服务提供(操作数据库)。
- TM:Transaction Manager,事务管理器,负责协调和管理事务,提供AP事务接口、管理RM事务提交。
3.1.1 X/Open事务执行流程
X/Open事务执行流程如下:
3.1.2 XA 协议
需要注意的是,在 X/Open DTP分布式事务模型中,TM 和 多个 RM 的事务控制,都是基于 XA 协议来完成的。XA 协议也是 X/Open 提出的分布式事务处理规范,也是分布式事务处理的工业标准,它定义了一组标准的函数接口,称为 xa 函数,用于管理和控制分布式事务的执行。
目前,主流数据库都实现了 XA 接口,如 MySQL、Oracle、DB2、PGSQL等,它们都可以作为RM。
常见的xa函数:
- xa_start:启动一个xa事务。示例(mysql):
xa start xid
,xid必须全局唯一 - xa_end:结束一个xa事务。示例(mysql):
xa end xid
- xa_perpare:准备。示例(mysql):
xa perpare xid
- xa_commit:提交xa事务。示例(mysql):
xa commit xid
- xa_rollback:回滚xa事务。示例(mysql):
xa rollback xid
3.2 两阶段提交协议
在 X/Open 分布式事务执行流程中,实际上会涉及两个阶段的提交,第一阶段是事务的准备阶段,第二个阶段是事务的提交/回滚阶段。这两个阶段都是由 TM(事务管理器) 发起的。
两阶段提交执行流程如下:
在 MySQL 2PC 中,第一阶段事务准备阶段,会写入 redo log、undo log 并持久化到磁盘。第二阶段事务提交 / 回滚阶段,如果提交会将数据持久化。值得思考的是,MySQL存在三大事务日志,还有一个binlog什么时候写入呢?
需要注意的是,具体的 binlog 写入时机可以通过 MySQL 的配置来指定,如下:
binlog_format
:用于指定 binlog 的日志格式。可选项:1.STATEMENT
,记录的是每个执行SQL语句,适用于大部分场景,但在某些情况下,可能会出现不确定性,例如涉及到随机数、时间戳等非确定性函数的操作时;2.ROW
,记录的是每个修改操作的行级别变更,这种格式提供了更精确的复制,但会增加 binlog 的大小;3.MIXED
,为STATEMENT
和ROW
的结合,MySQL会根据实际情况自动选择适合的记录方式。binlog_row_image
:在binlog_format
为ROW
或者MIXED
时,用于指定binlog记录的详细级别。可选项:1.FIULL
,记录包含完整的修改前和修改后的行数据;2.MINIMAL
,只记录被修改的列和相应的新值;3.NOBLOB
,排查表中 BLOB 列的内容,近记录 BLOB 列的存在和长度等信息。sync_binlog
:用于指定 binlog 的同步策略。可选项:0
,MySQL 不会主动将 Binlog 写入磁盘,而是交由操作系统来处理;1
,MySQL 在每次提交事务后都会将 Binlog 立即持久化到磁盘;N(N>1)
,设置为大于 1 的数值时,MySQL 将会等待累积到指定数量的 Binlog 事件后,才进行一次持久化写入操作。
两阶段提交将一个事务的处理过程拆分为两步,它的优点在于充分考虑到了分布式系统的不可靠因素,将事务提交失败的概率降小。当然,任何技术或思想的引入,随之而来的都会伴随着一定问题,只是在某些业务场景下,优点大于缺点。
2PC缺点如下:
- 同步阻塞:所有 RM 都是事务阻塞的,对于任何一次指令都必须要有明确的状态响应才能继续下一步,否则都会处于阻塞状态,导致占用资源处于锁定状态
- TM单点故障问题:如果TM在第二阶段时出现了宕机,那么其他 RM 资源都会一直处于锁定状态
- 网络波动导致数据不一致问题:在第二阶段中,TM 向所有 RM 发起 commit / rollback 指令,如果只有一部分 RM 响应成功,其他响应失败或没有接收到请求,会导致数据不一致问题。
3.3 三阶段提交协议
在 2PC 的基础上又引入了 3PC 协议对齐进行改善、增强。三阶段提交协议包括以下三个阶段:
- CanCommit(是否可以提交):询问 RM 是否可以提交事务。RM在此阶段只需检查是否存在某些因素导会导致事务无法成功提交,返回 yes / no。且引入了超时机制。
- PreCommit(预提交):在 CanCommit 阶段,所有 RM 都返回 yes,则进入 PreCommit 阶段,当前阶段会执行事务中的实际操作,写入相关日志,但不会提交事务。
- DoCommit(执行提交):TM 根据 RM 在 PreCommit 阶段的响应,决定是否执行最终提交操作。如果所有 RM 都响应 PreCommit 成功,TM 发送 DoCommit 请求给所有 RM 执行 提交操作。否则,如果有任何一个 RM 响应 PreCommit 失败或超时,TM 发送 DoAbort 请求给所有 RM 执行回滚操作。
与两阶段提交(2PC)协议相比,3PC 协议引入了一个额外的准备阶段(PreCommit)、超时机制,使得参与者在事务提交之前能够检查和预处理事务,并减少了参与者在等待最终提交阶段的时间,且当任何操作执行超时,TM 和 RM会继续提交事务(概率问题,超时成功概率比较大)。这样可以减少分布式事务中的阻塞时间,并提高系统的可用性和性能。
然而,3PC 协议仍然存在一些问题,如阻塞和单点故障的可能性,以及在协调者发生故障时的恢复问题。
四、分布式事务场景解决方案
4.1 TCC补偿方案
TCC (Try-Confirm-Cancel)是一种比较成熟的分布式一致性解决方案,使用 2PC 提交协议,将一个完整的分布式事务拆分为三个步骤,如下:
- Try :尝试,主要多数据进行校验或者资源的预留。
- Confirm :提交,执行事务。
- Cancel:取消,回滚事务,释放资源。
TCC 事务框架通过记录分布式事务操作日志,来解决事务协调者宕机或者 RM 没有收到 Cancel / Confirm 指令下的数据一致性问题,通过事务操作日志进行重试,已达到最终一致性(BASE理论)。因此,所有TCC接口均因保持幂等性。
4.2 基于可靠性消息的最终一致性方案
基于可靠性消息中间件来保证分布式事务的最终一致性是日常工作中比较常见的一种解决方案。
常用的消息中间件有 RabbitMQ、RocketMQ、Kafka
4.3 最大努力通知
最大努力通知是一种非常常见、简单的分布式事务解决方案,比较适用于对数据一致性要求不高的场景,利用了 衰减重试机制 达到事务的最终一致性。
五、分布式事务框架(Seate)
Seate 是一个开源的分布式事务解决方案,它提供了高性能、高可靠的分布式事务支持,适用于各种分布式场景。提供了TCC、XA、AT、Saga 四种事务模式。
Seata 主要有三个核心组件:
TC(Transaction Coordinator)
:事务协调器,负责全局事务的协调和控制(提交、回滚),以及负责事务操作日志的记录,便于故障恢复。RM(Resource Manager)
:资源管理器,负责资源的管理(锁定、释放)、事务的提交/回滚。TM(Transaction Manager)
:事务管理器,TM为全局事务的发起方和管理者,负责开启全局事务,并协调各个分支事务的执行和状态。负责全局事务的提交或回滚操作,并最终决定全局事务的最终状态。
Seata 提供了基于代理和注解的编程模型,以便开发者方便地使用分布式事务。通过与各种数据源和中间件的集成,Seata 可以实现跨多个数据源和服务的事务一致性,并提供高性能和可靠性的事务支持。
总而言之,Seata 是一个强大的分布式事务框架,可以帮助开发者解决分布式系统中的事务一致性问题,简化开发过程,提高系统的可靠性和性能。
5.1 TCC模式
TCC 是一种补偿型的事务模式,它通过在业务逻辑中显式地定义 Try、Confirm 和 Cancel 三个阶段,来实现分布式事务的控制和保证。
- Try(尝试阶段):
- 在 Try 阶段,业务逻辑会尝试预留资源和执行操作,但并不真正提交事务。
- Try 阶段需要实现一个与业务逻辑相对应的 Try 方法,用于预留资源和执行业务操作。
- Confirm(确认阶段):
- 如果所有的分支事务(Try 阶段)都成功完成,那么在 Confirm 阶段,所有的分支事务都会被确认提交。
- Confirm 阶段需要实现一个与业务逻辑相对应的 Confirm 方法,用于确认提交事务。
- Cancel(取消阶段):
- 如果任何一个分支事务(Try 阶段)失败,或者在 Confirm 阶段出现异常,那么在 Cancel 阶段,所有的分支事务都会被回滚。
- Cancel 阶段需要实现一个与业务逻辑相对应的 Cancel 方法,用于取消回滚事务。
通过定义 Try、Confirm 和 Cancel 三个阶段的业务逻辑,Seata TCC 可以在分布式事务发起方和参与方之间实现事务的协调和一致性。
5.2 XA模式
XA 是一种经典的事务处理协议,它通过两阶段提交(2PC)协议来确保分布式事务的一致性。
- Prepare(准备阶段):
- 在 Prepare 阶段,每个参与分布式事务的资源管理器(RM)会将事务的操作请求发送给事务协调器(TC)。
- TC 负责协调各个 RM 的 Prepare 操作,要求每个 RM 准备好执行事务,并将准备结果返回给 TC。
- 如果所有的 RM 都准备就绪,TC 会通知各个 RM 进入下一阶段。如果有任何一个 RM 出现问题,则整个事务会中止。
- Commit(提交阶段):
- 在 Commit 阶段,TC 会向各个 RM 发送 Commit 请求,要求各个 RM 执行事务的提交操作。
- 每个 RM 接收到 Commit 请求后,会执行事务的提交,并将提交结果返回给 TC。
- 如果所有的 RM 都成功提交事务,TC 最终会向应用程序返回成功的响应。如果有任何一个 RM 出现问题,则整个事务会中止。
Seata XA 使用 XA 协议来实现分布式事务的一致性和隔离性。它通过 TC 和各个 RM 的协调和通信,保证了所有参与分布式事务的资源在提交阶段的一致性,即要么全部提交成功,要么全部失败。
5.3 AT模式
AT 是一种基于应用程序代码逻辑的事务模式,通过在业务逻辑中插入事务协调指令,来实现分布式事务的控制和保证。
在 Seata AT 模式中,每个参与分布式事务的业务逻辑服务都需要进行事务注解的标记,以告知 Seata 框架如何协调和执行事务。Seata AT 会通过拦截和增强事务性方法的执行过程来保证事务的一致性。
AT 模式主要特征如下:
- Seata AT 模式对业务逻辑代码的侵入性较低,只需在需要参与事务的业务方法上添加相应的事务注解即可。
- 乐观锁机制:Seata AT 模式使用乐观锁机制来处理分布式事务的冲突;在事务提交之前,Seata 会检查数据是否被其他事务修改过,以避免数据的冲突和不一致性。
- 在发生分布式事务回滚时,Seata AT 会通过记录事务日志和使用补偿机制来实现撤销操作。
AT模式是一种改进版的两阶段提交协议,如下:
- 第一阶段:业务操作、回滚日志在同一个事务中进行本地提交,是否本地锁、资源。
- 第二阶段:提交异步化,非常快速完成。回滚通过第一阶段的回滚日志进行反向补偿。
5.4 Saga长事务模式
Saga 模式是一种补偿型的事务模式,用于处理分布式环境下的长流程事务。
在长流程事务中,各个阶段的操作可能会涉及多个服务和多个事务,而且每个阶段的操作都是可撤销的。Saga 事务模型如下:
Saga 长事务模式由一系列 Ti 组成,每个 Ti 都有对应的补偿事务 Cj(i > j > 1)。Saga 整体采用事件驱动的方式进行事务的协调和执行,
每个阶段的操作和补偿操作都会触发相应的事件,以触发下一个阶段的执行或回滚。Saga 提供了两种补偿恢复方式:
- 向后恢复:任一 Ti 失败,执行 Cj 进行撤销。
- 向前恢复:不进行补偿恢复,而是对所有失败的事务进行重试。这种方式比较适用于一定要执行成功的业务场景。
关于 Seata 详细介绍,感兴趣的小伙伴可以参考一下官网 https://seata.io/zh-cn/docs/overview/what-is-seata.html