来了!阿里开源分布式事务解决方案 Fescar

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 广为人知的阿里分布式事务解决方案:GTS(Global Transaction Service),已正式推出开源版本,取名为“Fescar”,希望帮助业界解决微服务架构下的分布式事务问题,今天我们一起来深入了解。

640_jpeg

微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇,系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并操作多个数据库实现,服务调用的分布式事务问题变的非常突出。分布式事务已经成为微服务落地最大的阻碍,也是最具挑战性的一个技术难题。

  1. 什么是微服务化带来的分布式事务问题?

首先,设想一个传统的单体应用(Monolithic App),通过 3 个 Module,在同一个数据源上更新数据来完成一项业务。

很自然的,整个业务过程的数据一致性由本地事务来保证。

640

随着业务需求和架构的变化,单体应用被拆分为微服务:原来的 3 个 Module 被拆分为 3 个独立的服务,分别使用独立的数据源(Pattern: Database per service)。业务过程将由 3 个服务的调用来完成。

640_2

此时,每一个服务内部的数据一致性仍有本地事务来保证。而整个业务层面的全局数据一致性要如何保障呢?这就是微服务架构下面临的,典型的分布式事务需求:我们需要一个分布式事务的解决方案保障业务全局的数据一致性。

微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇,系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并操作多个数据库实现,服务调用的分布式事务问题变的非常突出。分布式事务已经成为微服务落地最大的阻碍,也是最具挑战性的一个技术难题。

  1. 什么是微服务化带来的分布式事务问题?

首先,设想一个传统的单体应用(Monolithic App),通过 3 个 Module,在同一个数据源上更新数据来完成一项业务。

很自然的,整个业务过程的数据一致性由本地事务来保证。

随着业务需求和架构的变化,单体应用被拆分为微服务:原来的 3 个 Module 被拆分为 3 个独立的服务,分别使用独立的数据源(Pattern: Database per service)。业务过程将由 3 个服务的调用来完成。

此时,每一个服务内部的数据一致性仍有本地事务来保证。而整个业务层面的全局数据一致性要如何保障呢?这就是微服务架构下面临的,典型的分布式事务需求:我们需要一个分布式事务的解决方案保障业务全局的数据一致性。

  1. Fescar 的发展历程

阿里是国内最早一批进行应用分布式(微服务化)改造的企业,所以很早就遇到微服务架构下的分布式事务问题。

2014 年,阿里中间件团队发布 TXC(Taobao Transaction Constructor),为集团内应用提供分布式事务服务。

2016 年,TXC 经过产品化改造,以 GTS(Global Transaction Service)的身份登陆阿里云,成为当时业界唯一一款云上分布式事务产品,在阿云里的公有云、专有云解决方案中,开始服务于众多外部客户。

2019 年起,基于 TXC 和 GTS 的技术积累,阿里中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback, FESCAR),和社区一起建设这个分布式事务解决方案。

TXC/GTS/Fescar 一脉相承,为解决微服务架构下的分布式事务问题交出了一份与众不同的答卷。

2.1 设计初衷

高速增长的互联网时代,快速试错的能力对业务来说是至关重要的:

• 一方面,不应该因为技术架构上的微服务化和分布式事务支持的引入,给业务层面带来额外的研发负担。
• 另一方面,引入分布式事务支持的业务应该基本保持在同一量级上的性能表现,不能因为事务机制显著拖慢业务。

基于这两点,我们设计之初的最重要的考量就在于:

• 对业务无侵入:这里的“侵入”是指,因为分布式事务这个技术问题的制约,要求应用在业务层面进行设计和改造。这种设计和改造往往会给应用带来很高的研发和维护成本。我们希望把分布式事务问题在 中间件 这个层次解决掉,不要求应用在业务层面做额外的工作。
• 高性能:引入分布式事务的保障,必然会有额外的开销,引起性能的下降。我们希望把分布式事务引入的性能损耗降到非常低的水平,让应用不因为分布式事务的引入导致业务的可用性受影响。

2.2 既有的解决方案为什么不满足?

既有的分布式事务解决方案按照对业务侵入性分为两类,即:对业务无侵入的和对业务有侵入的。

业务无侵入的方案

既有的主流分布式事务解决方案中,对业务无侵入的只有基于 XA 的方案,但应用 XA 方案存在 3 个方面的问题:

• 要求数据库提供对 XA 的支持。如果遇到不支持 XA(或支持得不好,比如 MySQL 5.7 以前的版本)的数据库,则不能使用。
• 受协议本身的约束,事务资源的锁定周期长。长周期的资源锁定从业务层面来看,往往是不必要的,而因为事务资源的管理器是数据库本身,应用层无法插手。这样形成的局面就是,基于 XA 的应用往往性能会比较差,而且很难优化。
• 已经落地的基于 XA 的分布式解决方案,都依托于重量级的应用服务器(Tuxedo/WebLogic/WebSphere 等),这是不适用于微服务架构的。

侵入业务的方案

实际上,最初分布式事务只有 XA 这个唯一方案。XA 是完备的,但在实践过程中,由于种种原因(包含但不限于上面提到的 3 点)往往不得不放弃,转而从业务层面着手来解决分布式事务问题。比如:

• 基于可靠消息的最终一致性方案
• TCC
• Saga

都属于这一类。这些方案的具体机制在这里不做展开,网上这方面的论述文章非常多。总之,这些方案都要求在应用的业务层面把分布式事务技术约束考虑到设计中,通常每一个服务都需要设计实现正向和反向的幂等接口。这样的设计约束,往往会导致很高的研发和维护成本。

2.3 理想的方案应该是什么样子?

不可否认,侵入业务的分布式事务方案都经过大量实践验证,能有效解决问题,在各种行业的业务应用系统中起着重要作用。但回到原点来思考,这些方案的采用实际上都是迫于无奈。设想,如果基于 XA 的方案能够不那么重,并且能保证业务的性能需求,相信不会有人愿意把分布式事务问题拿到业务层面来解决。

一个理想的分布式事务解决方案应该:像使用本地事务一样简单,业务逻辑只关注业务层面的需求,不需要考虑事务机制上的约束。

  1. 原理和设计

我们要设计一个对业务无侵入的方案,所以从业务无侵入的 XA 方案来思考:是否可以在 XA 的基础上演进,解决掉 XA 方案面临的问题呢?

3.1 如何定义一个分布式事务?

首先,很自然的,我们可以把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的 分支事务 达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个满足 ACID 的本地事务。这是我们对分布式事务结构的基本认识,与 XA 是一致的。

其次,与 XA 的模型类似,我们定义 3 个组件来协议分布式事务的处理过程。

• Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
• Transaction Manager (TM):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
• Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

一个典型的分布式事务过程:

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
  2. XID 在微服务调用链路的上下文中传播。
  3. RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖。
  4. TM 向 TC 发起针对 XID 的全局提交或回滚决议。
  5. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

至此,Fescar 的协议机制总体上看与 XA 是一致的。

3.2 与 XA 的差别在什么地方?

架构层次

XA 方案的 RM 实际上是在数据库层,RM 本质上就是数据库自身(通过提供支持 XA 的驱动程序来供应用使用)。

而 Fescar 的 RM 是以二方包的形式作为中间件层部署在应用程序这一侧的,不依赖与数据库本身对协议的支持,当然也不需要数据库支持 XA 协议。这点对于微服务化的架构来说是非常重要的:应用层不需要为本地事务和分布式事务两类不同场景来适配两套不同的数据库驱动。

这个设计,剥离了分布式事务方案对数据库在 协议支持 上的要求。

两阶段提交

先来看一下 XA 的 2PC 过程。

无论 Phase2 的决议是 commit 还是 rollback,事务性资源的锁都要保持到 Phase2 完成才释放。

设想一个正常运行的业务,大概率是 90% 以上的事务最终应该是成功提交的,我们是否可以在 Phase1 就将本地事务提交呢?这样 90% 以上的情况下,可以省去 Phase2 持锁的时间,整体提高效率。

这个设计,在绝大多数场景减少了事务持锁时间,从而提高了事务的并发度。

当然,你肯定会问:Phase1 即提交的情况下,Phase2 如何回滚呢?

3.3 分支事务如何提交和回滚?

首先,应用需要使用 Fescar 的 JDBC 数据源代理,也就是 Fescar 的 RM。

Phase1:

Fescar 的 JDBC 数据源代理通过对业务 SQL 的解析,把业务数据在更新前后的数据镜像组织成回滚日志,利用本地事务 的 ACID 特性,将业务数据的更新和回滚日志的写入在同一个 本地事务中提交。

这样,可以保证:任何提交的业务数据的更新一定有相应的回滚日志存在。

基于这样的机制,分支的本地事务便可以在全局事务的 Phase1 提交,马上释放本地事务锁定的资源。

Phase2:

如果决议是全局提交,此时分支事务此时已经完成提交,不需要同步协调处理(只需要异步清理回滚日志),Phase2 可以非常快速地完成。

如果决议是全局回滚,RM 收到协调器发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚。

3.4 事务传播机制

XID 是一个全局事务的唯一标识,事务传播机制要做的就是把 XID 在服务调用链路中传递下去,并绑定到服务的事务上下文中,这样,服务链路中的数据库更新操作,就都会向该 XID 代表的全局事务注册分支,纳入同一个全局事务的管辖。

基于这个机制,Fescar 是可以支持任何微服务 RPC 框架的。只要在特定框架中找到可以透明传播 XID 的机制即可,比如,Dubbo 的 Filter + RpcContext。

对应到 Java EE 规范和 Spring 定义的事务传播属性,Fescar 的支持如下:

• PROPAGATION_REQUIRED:默认支持
• PROPAGATION_SUPPORTS:默认支持
• PROPAGATION_MANDATORY:应用通过 API 来实现
• PROPAGATION_REQUIRES_NEW:应用通过 API 来实现
• PROPAGATION_NOT_SUPPORTED:应用通过 API 来实现
• PROPAGATION_NEVER:应用通过 API 来实现
• PROPAGATION_REQUIRED_NESTED:不支持

3.5 隔离性

全局事务的隔离性是建立在分支事务的本地隔离级别基础之上的。

在数据库本地隔离级别读已提交或以上的前提下,Fescar 设计了由事务协调器维护的 全局写排他锁,来保证事务间的写隔离,将全局事务默认定义在读未提交的隔离级别上。

我们对隔离级别的共识是:绝大部分应用在 读已提交 的隔离级别下工作是没有问题的。而实际上,这当中又有绝大多数的应用场景,实际上工作在读未提交的隔离级别下同样没有问题。

在极端场景下,应用如果需要达到全局的 读已提交,Fescar 也提供了相应的机制来达到目的。默认,Fescar 是工作在 读无提交 的隔离级别下,保证绝大多数场景的高效性。

事务的 ACID 属性在 Fescar 中的体现是一个比较复杂的话题,我们会有专门的文章来深入分析,这里不做进一步展开。

  1. 适用场景分析

前文所述的 Fescar 的核心原理中有一个重要前提:分支事务中涉及的资源,必须是支持ACID 事务的 关系型数据库。分支的提交和回滚机制,都依赖于本地事务的保障。所以,如果应用使用的数据库是不支持事务的,或根本不是关系型数据库,就不适用。

另外,目前 Fescar 的实现还存在一些局限,比如:事务隔离级别最高支持到读已提交的水平,SQL 的解析还不能涵盖全部的语法等。

为了覆盖 Fescar 原生机制暂时不能支持应用场景,我们定义了另外一种工作模式。

上面介绍的 Fescar 原生工作模式称为 AT(Automatic Transaction)模式,这种模式是对业务无侵入的。与之相应的另外一种工作模式称为 MT(Manual Transaction)模式,这种模式下,分支事务需要应用自己来定义业务本身及提交和回滚的逻辑。

4.1 分支的基本行为模式

作为全局事务一部分的分支事务,除本身的业务逻辑外,都包含 4 个与协调器交互的行为:

• 分支注册:在分支事务的数据操作进行之前,需要向协调器注册,把即将进行的分支事务数据操作,纳入一个已经开启的全局事务的管理中去,在分支注册成功后,才可以进行数据操作。
• 状态上报:在分支事务的数据操作完成后,需要向事务协调器上报其执行结果。
• 分支提交:响应协调器发出的分支事务提交的请求,完成分支提交。
• 分支回滚:响应协调器发出的分支事务回滚的请求,完成分支回滚。

4.2 AT 模式分支的行为模式

业务逻辑不需要关注事务机制,分支与全局事务的交互过程自动进行。

4.3 MT 模式分支的行为模式

业务逻辑需要被分解为 Prepare/Commit/Rollback 3 部分,形成一个 MT 分支,加入全局事务。

MT 模式一方面是 AT 模式的补充。另外,更重要的价值在于,通过 MT 模式可以把众多非事务性资源纳入全局事务的管理中。

4.4 混合模式

因为 AT 和 MT 模式的分支从根本上行为模式是一致的,所以可以完全兼容,即,一个全局事务中,可以同时存在 AT 和 MT 的分支。这样就可以达到全面覆盖业务场景的目的:AT 模式可以支持的,使用 AT 模式;AT 模式暂时支持不了的,用 MT 模式来替代。另外,自然的,MT 模式管理的非事务性资源也可以和支持事务的关系型数据库资源一起,纳入同一个分布式事务的管理中。

4.5 应用场景的远景

回到我们设计的初衷:一个理想的分布式事务解决方案是不应该侵入业务的。MT 模式是在 AT 模式暂时不能完全覆盖所有场景的情况下,一个比较自然的补充方案。我们希望通过 AT 模式的不断演进增强,逐步扩大所支持的场景,MT 模式逐步收敛。未来,我们会纳入对 XA 的原生支持,用 XA 这种无侵入的方式来覆盖 AT 模式无法触达的场景。

  1. 扩展点

5.1 微服务框架的支持

事务上下文在微服务间的传播需要根据微服务框架本身的机制,订制最优的,对应用层透明的解决方案。有兴趣在这方面共建的开发者可以参考内置的对 Dubbo 的支持方案,来实现对其他微服务框架的支持。

5.2 所支持的数据库类型

因为 AT 涉及 SQL 的解析,所以在不同类型的数据库上工作,会有一些特定的适配。有兴趣在这方面共建的开发者可以参考内置的对 MySQL 的支持方案,来实现对其他数据库的支持。

5.3 配置和服务注册发现

支持接入不同的配置和服务注册发现解决方案。比如:Nacos、Eureka、ZooKeeper 等。

5.4 MT 模式的场景拓展

MT 模式的一个重要作用就是,可以把非关系型数据库的资源,通过 MT 模式分支的包装,纳入到全局事务的管辖中来。比如,Redis、HBase、RocketMQ 的事务消息等。有兴趣在这方面共建的开发者可以在这里贡献一系列相关生态的适配方案。

5.5 事务协调器的分布式高可用方案

针对不同场景,支持不同的方式作为事务协调器 Server 端的高可用方案。比如,针对事务状态的持久化,可以是基于文件的实现方案,也可以是基于数据库的实现方案;集群间的状态同步,可以是基于 RPC 通信的方案,也可以是基于高可用 KV 存储的方案。

  1. Roadmap

蓝图

绿色部分是已经开源发布出来的,黄色 部分是将在后续版本中由阿里发布出来的,蓝色部分是我们和社区共建生态部分:

• 对不同数据库的支持,开发者可以参考 MySQL 的实现。
• 对不同微服务框架的支持,开发者可以参考 Dubbo 的实现。
• 对 MQ、NoSQL 的支持,开发者可以参考 TCC 的实现。
• 配置和服务注册发现:开发者通过少量的工作可以接入任何可以提供这类服务的框架。
• 当然,非 蓝色 的部分也非常欢迎社区参与进来,贡献更优的解决方案。
• 另外,XA 作为分布式事务的标准,是一个完备的分布式事务解决方案不可或缺的,远景的规划中,我们一定需要把 XA 的支持加入进来。

初步的版本规划

v0.1.0:
• 微服务框架支持: Dubbo
• 数据库支持: MySQL
• 基于 Spring AOP 的 Annotation
• 事务协调器: 单机版本

v0.5.x:
• 微服务框架支持: Spring Cloud
• MT 模式
• 支持 TCC 模式事务的适配
• 动态配置和服务发现
• 事务协调器: 高可用集群版本

v0.8.x:
• Metrics
• 控制台: 监控/部署/升级/扩缩容

v1.0.0:
• General Availability: 生产环境适用

v1.5.x:
• 数据库支持: Oracle/PostgreSQL/OceanBase
• 不依赖 Spring AOP 的 Annotation
• 热点数据的优化处理机制
• RocketMQ 事务消息纳入全局事务管理
• NoSQL 纳入全局事务管理的适配机制
• 支持 HBase
• 支持 Redis

v2.0.0:
• 支持 XA

当然,项目迭代演进的过程,我们最重视的是社区的声音,路线图会和社区充分交流及时进行调整。

相关链接:
FESCAR on GitHub:
https://github.com/alibaba/fescar

GTS on Aliyun:
https://help.aliyun.com/product/48444.html

  1. Fescar 的发展历程

阿里是国内最早一批进行应用分布式(微服务化)改造的企业,所以很早就遇到微服务架构下的分布式事务问题。

2014 年,阿里中间件团队发布 TXC(Taobao Transaction Constructor),为集团内应用提供分布式事务服务。

2016 年,TXC 经过产品化改造,以 GTS(Global Transaction Service)的身份登陆阿里云,成为当时业界唯一一款云上分布式事务产品,在阿云里的公有云、专有云解决方案中,开始服务于众多外部客户。

2019 年起,基于 TXC 和 GTS 的技术积累,阿里中间件团队发起了开源项目 Fescar(Fast & EaSy Commit And Rollback, FESCAR),和社区一起建设这个分布式事务解决方案。

TXC/GTS/Fescar 一脉相承,为解决微服务架构下的分布式事务问题交出了一份与众不同的答卷。

2.1 设计初衷

高速增长的互联网时代,快速试错的能力对业务来说是至关重要的:

• 一方面,不应该因为技术架构上的微服务化和分布式事务支持的引入,给业务层面带来额外的研发负担。
• 另一方面,引入分布式事务支持的业务应该基本保持在同一量级上的性能表现,不能因为事务机制显著拖慢业务。

基于这两点,我们设计之初的最重要的考量就在于:

• 对业务无侵入:这里的“侵入”是指,因为分布式事务这个技术问题的制约,要求应用在业务层面进行设计和改造。这种设计和改造往往会给应用带来很高的研发和维护成本。我们希望把分布式事务问题在 中间件 这个层次解决掉,不要求应用在业务层面做额外的工作。
• 高性能:引入分布式事务的保障,必然会有额外的开销,引起性能的下降。我们希望把分布式事务引入的性能损耗降到非常低的水平,让应用不因为分布式事务的引入导致业务的可用性受影响。

2.2 既有的解决方案为什么不满足?

既有的分布式事务解决方案按照对业务侵入性分为两类,即:对业务无侵入的和对业务有侵入的。

业务无侵入的方案

既有的主流分布式事务解决方案中,对业务无侵入的只有基于 XA 的方案,但应用 XA 方案存在 3 个方面的问题:

• 要求数据库提供对 XA 的支持。如果遇到不支持 XA(或支持得不好,比如 MySQL 5.7 以前的版本)的数据库,则不能使用。
• 受协议本身的约束,事务资源的锁定周期长。长周期的资源锁定从业务层面来看,往往是不必要的,而因为事务资源的管理器是数据库本身,应用层无法插手。这样形成的局面就是,基于 XA 的应用往往性能会比较差,而且很难优化。
• 已经落地的基于 XA 的分布式解决方案,都依托于重量级的应用服务器(Tuxedo/WebLogic/WebSphere 等),这是不适用于微服务架构的。

侵入业务的方案

实际上,最初分布式事务只有 XA 这个唯一方案。XA 是完备的,但在实践过程中,由于种种原因(包含但不限于上面提到的 3 点)往往不得不放弃,转而从业务层面着手来解决分布式事务问题。比如:

• 基于可靠消息的最终一致性方案
• TCC
• Saga

都属于这一类。这些方案的具体机制在这里不做展开,网上这方面的论述文章非常多。总之,这些方案都要求在应用的业务层面把分布式事务技术约束考虑到设计中,通常每一个服务都需要设计实现正向和反向的幂等接口。这样的设计约束,往往会导致很高的研发和维护成本。

2.3 理想的方案应该是什么样子?

不可否认,侵入业务的分布式事务方案都经过大量实践验证,能有效解决问题,在各种行业的业务应用系统中起着重要作用。但回到原点来思考,这些方案的采用实际上都是迫于无奈。设想,如果基于 XA 的方案能够不那么重,并且能保证业务的性能需求,相信不会有人愿意把分布式事务问题拿到业务层面来解决。

一个理想的分布式事务解决方案应该:像使用本地事务一样简单,业务逻辑只关注业务层面的需求,不需要考虑事务机制上的约束。

  1. 原理和设计

我们要设计一个对业务无侵入的方案,所以从业务无侵入的 XA 方案来思考:是否可以在 XA 的基础上演进,解决掉 XA 方案面临的问题呢?

3.1 如何定义一个分布式事务?

首先,很自然的,我们可以把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的 分支事务 达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个满足 ACID 的本地事务。这是我们对分布式事务结构的基本认识,与 XA 是一致的。

640_3_jpeg

其次,与 XA 的模型类似,我们定义 3 个组件来协议分布式事务的处理过程。

640_3

• Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
• Transaction Manager (TM):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
• Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

一个典型的分布式事务过程:

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
  2. XID 在微服务调用链路的上下文中传播。
  3. RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖。
  4. TM 向 TC 发起针对 XID 的全局提交或回滚决议。
  5. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

640_4

至此,Fescar 的协议机制总体上看与 XA 是一致的。

3.2 与 XA 的差别在什么地方?

架构层次

640_5

XA 方案的 RM 实际上是在数据库层,RM 本质上就是数据库自身(通过提供支持 XA 的驱动程序来供应用使用)。

而 Fescar 的 RM 是以二方包的形式作为中间件层部署在应用程序这一侧的,不依赖与数据库本身对协议的支持,当然也不需要数据库支持 XA 协议。这点对于微服务化的架构来说是非常重要的:应用层不需要为本地事务和分布式事务两类不同场景来适配两套不同的数据库驱动。

这个设计,剥离了分布式事务方案对数据库在 协议支持 上的要求。

两阶段提交

先来看一下 XA 的 2PC 过程。

640_6

无论 Phase2 的决议是 commit 还是 rollback,事务性资源的锁都要保持到 Phase2 完成才释放。

设想一个正常运行的业务,大概率是 90% 以上的事务最终应该是成功提交的,我们是否可以在 Phase1 就将本地事务提交呢?这样 90% 以上的情况下,可以省去 Phase2 持锁的时间,整体提高效率。

640_7

这个设计,在绝大多数场景减少了事务持锁时间,从而提高了事务的并发度。

当然,你肯定会问:Phase1 即提交的情况下,Phase2 如何回滚呢?

3.3 分支事务如何提交和回滚?

首先,应用需要使用 Fescar 的 JDBC 数据源代理,也就是 Fescar 的 RM。

640_8

Phase1:

Fescar 的 JDBC 数据源代理通过对业务 SQL 的解析,把业务数据在更新前后的数据镜像组织成回滚日志,利用本地事务 的 ACID 特性,将业务数据的更新和回滚日志的写入在同一个 本地事务中提交。

这样,可以保证:任何提交的业务数据的更新一定有相应的回滚日志存在。

640_4_jpeg

基于这样的机制,分支的本地事务便可以在全局事务的 Phase1 提交,马上释放本地事务锁定的资源。

Phase2:

如果决议是全局提交,此时分支事务此时已经完成提交,不需要同步协调处理(只需要异步清理回滚日志),Phase2 可以非常快速地完成。

640_9

如果决议是全局回滚,RM 收到协调器发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚。

640_5_jpeg

3.4 事务传播机制

XID 是一个全局事务的唯一标识,事务传播机制要做的就是把 XID 在服务调用链路中传递下去,并绑定到服务的事务上下文中,这样,服务链路中的数据库更新操作,就都会向该 XID 代表的全局事务注册分支,纳入同一个全局事务的管辖。

基于这个机制,Fescar 是可以支持任何微服务 RPC 框架的。只要在特定框架中找到可以透明传播 XID 的机制即可,比如,Dubbo 的 Filter + RpcContext。

对应到 Java EE 规范和 Spring 定义的事务传播属性,Fescar 的支持如下:

• PROPAGATION_REQUIRED:默认支持
• PROPAGATION_SUPPORTS:默认支持
• PROPAGATION_MANDATORY:应用通过 API 来实现
• PROPAGATION_REQUIRES_NEW:应用通过 API 来实现
• PROPAGATION_NOT_SUPPORTED:应用通过 API 来实现
• PROPAGATION_NEVER:应用通过 API 来实现
• PROPAGATION_REQUIRED_NESTED:不支持

3.5 隔离性

全局事务的隔离性是建立在分支事务的本地隔离级别基础之上的。

在数据库本地隔离级别读已提交或以上的前提下,Fescar 设计了由事务协调器维护的 全局写排他锁,来保证事务间的写隔离,将全局事务默认定义在读未提交的隔离级别上。

我们对隔离级别的共识是:绝大部分应用在 读已提交 的隔离级别下工作是没有问题的。而实际上,这当中又有绝大多数的应用场景,实际上工作在读未提交的隔离级别下同样没有问题。

在极端场景下,应用如果需要达到全局的 读已提交,Fescar 也提供了相应的机制来达到目的。默认,Fescar 是工作在 读无提交 的隔离级别下,保证绝大多数场景的高效性。

640_6_jpeg

事务的 ACID 属性在 Fescar 中的体现是一个比较复杂的话题,我们会有专门的文章来深入分析,这里不做进一步展开。

  1. 适用场景分析

前文所述的 Fescar 的核心原理中有一个重要前提:分支事务中涉及的资源,必须是支持ACID 事务的 关系型数据库。分支的提交和回滚机制,都依赖于本地事务的保障。所以,如果应用使用的数据库是不支持事务的,或根本不是关系型数据库,就不适用。

另外,目前 Fescar 的实现还存在一些局限,比如:事务隔离级别最高支持到读已提交的水平,SQL 的解析还不能涵盖全部的语法等。

为了覆盖 Fescar 原生机制暂时不能支持应用场景,我们定义了另外一种工作模式。

上面介绍的 Fescar 原生工作模式称为 AT(Automatic Transaction)模式,这种模式是对业务无侵入的。与之相应的另外一种工作模式称为 MT(Manual Transaction)模式,这种模式下,分支事务需要应用自己来定义业务本身及提交和回滚的逻辑。

4.1 分支的基本行为模式

作为全局事务一部分的分支事务,除本身的业务逻辑外,都包含 4 个与协调器交互的行为:

• 分支注册:在分支事务的数据操作进行之前,需要向协调器注册,把即将进行的分支事务数据操作,纳入一个已经开启的全局事务的管理中去,在分支注册成功后,才可以进行数据操作。
• 状态上报:在分支事务的数据操作完成后,需要向事务协调器上报其执行结果。
• 分支提交:响应协调器发出的分支事务提交的请求,完成分支提交。
• 分支回滚:响应协调器发出的分支事务回滚的请求,完成分支回滚。

640_10

4.2 AT 模式分支的行为模式

业务逻辑不需要关注事务机制,分支与全局事务的交互过程自动进行。

640_7_jpeg

4.3 MT 模式分支的行为模式

业务逻辑需要被分解为 Prepare/Commit/Rollback 3 部分,形成一个 MT 分支,加入全局事务。

640_11

MT 模式一方面是 AT 模式的补充。另外,更重要的价值在于,通过 MT 模式可以把众多非事务性资源纳入全局事务的管理中。

4.4 混合模式

因为 AT 和 MT 模式的分支从根本上行为模式是一致的,所以可以完全兼容,即,一个全局事务中,可以同时存在 AT 和 MT 的分支。这样就可以达到全面覆盖业务场景的目的:AT 模式可以支持的,使用 AT 模式;AT 模式暂时支持不了的,用 MT 模式来替代。另外,自然的,MT 模式管理的非事务性资源也可以和支持事务的关系型数据库资源一起,纳入同一个分布式事务的管理中。

4.5 应用场景的远景

回到我们设计的初衷:一个理想的分布式事务解决方案是不应该侵入业务的。MT 模式是在 AT 模式暂时不能完全覆盖所有场景的情况下,一个比较自然的补充方案。我们希望通过 AT 模式的不断演进增强,逐步扩大所支持的场景,MT 模式逐步收敛。未来,我们会纳入对 XA 的原生支持,用 XA 这种无侵入的方式来覆盖 AT 模式无法触达的场景。

640_12

  1. 扩展点

5.1 微服务框架的支持

事务上下文在微服务间的传播需要根据微服务框架本身的机制,订制最优的,对应用层透明的解决方案。有兴趣在这方面共建的开发者可以参考内置的对 Dubbo 的支持方案,来实现对其他微服务框架的支持。

5.2 所支持的数据库类型

因为 AT 涉及 SQL 的解析,所以在不同类型的数据库上工作,会有一些特定的适配。有兴趣在这方面共建的开发者可以参考内置的对 MySQL 的支持方案,来实现对其他数据库的支持。

5.3 配置和服务注册发现

支持接入不同的配置和服务注册发现解决方案。比如:Nacos、Eureka、ZooKeeper 等。

5.4 MT 模式的场景拓展

MT 模式的一个重要作用就是,可以把非关系型数据库的资源,通过 MT 模式分支的包装,纳入到全局事务的管辖中来。比如,Redis、HBase、RocketMQ 的事务消息等。有兴趣在这方面共建的开发者可以在这里贡献一系列相关生态的适配方案。

5.5 事务协调器的分布式高可用方案

针对不同场景,支持不同的方式作为事务协调器 Server 端的高可用方案。比如,针对事务状态的持久化,可以是基于文件的实现方案,也可以是基于数据库的实现方案;集群间的状态同步,可以是基于 RPC 通信的方案,也可以是基于高可用 KV 存储的方案。

  1. Roadmap

蓝图

640_8_jpeg

绿色部分是已经开源发布出来的,黄色 部分是将在后续版本中由阿里发布出来的,蓝色部分是我们和社区共建生态部分:

• 对不同数据库的支持,开发者可以参考 MySQL 的实现。
• 对不同微服务框架的支持,开发者可以参考 Dubbo 的实现。
• 对 MQ、NoSQL 的支持,开发者可以参考 TCC 的实现。
• 配置和服务注册发现:开发者通过少量的工作可以接入任何可以提供这类服务的框架。
• 当然,非 蓝色 的部分也非常欢迎社区参与进来,贡献更优的解决方案。
• 另外,XA 作为分布式事务的标准,是一个完备的分布式事务解决方案不可或缺的,远景的规划中,我们一定需要把 XA 的支持加入进来。

初步的版本规划

v0.1.0:
• 微服务框架支持: Dubbo
• 数据库支持: MySQL
• 基于 Spring AOP 的 Annotation
• 事务协调器: 单机版本

v0.5.x:
• 微服务框架支持: Spring Cloud
• MT 模式
• 支持 TCC 模式事务的适配
• 动态配置和服务发现
• 事务协调器: 高可用集群版本

v0.8.x:
• Metrics
• 控制台: 监控/部署/升级/扩缩容

v1.0.0:
• General Availability: 生产环境适用

v1.5.x:
• 数据库支持: Oracle/PostgreSQL/OceanBase
• 不依赖 Spring AOP 的 Annotation
• 热点数据的优化处理机制
• RocketMQ 事务消息纳入全局事务管理
• NoSQL 纳入全局事务管理的适配机制
• 支持 HBase
• 支持 Redis

v2.0.0:
• 支持 XA

当然,项目迭代演进的过程,我们最重视的是社区的声音,路线图会和社区充分交流及时进行调整。

相关链接:
FESCAR on GitHub:
https://github.com/alibaba/fescar

GTS on Aliyun:
https://help.aliyun.com/product/48444.html

点击了解“阿里云新品发布会频道”:
https://promotion.aliyun.com/ntms/act/cloud/product.html

相关文章
|
3月前
|
存储 SQL 微服务
常用的分布式事务解决方案(三)
常用的分布式事务解决方案(三)
|
3月前
|
关系型数据库 MySQL
常见分布式事务的解决方案(一)
常见分布式事务的解决方案(一)
|
1月前
|
消息中间件 监控 数据可视化
Apache Airflow 开源最顶级的分布式工作流平台
Apache Airflow 是一个用于创作、调度和监控工作流的平台,通过将工作流定义为代码,实现更好的可维护性和协作性。Airflow 使用有向无环图(DAG)定义任务,支持动态生成、扩展和优雅的管道设计。其丰富的命令行工具和用户界面使得任务管理和监控更加便捷。适用于静态和缓慢变化的工作流,常用于数据处理。
Apache Airflow 开源最顶级的分布式工作流平台
|
1月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
44 5
|
2月前
|
消息中间件 架构师 Java
阿里面试:秒杀的分布式事务, 是如何设计的?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴在面试阿里、滴滴、极兔等一线互联网企业时,遇到了许多关于分布式事务的重要面试题。为了帮助大家更好地应对这些面试题,尼恩进行了系统化的梳理,详细介绍了Seata和RocketMQ事务消息的结合,以及如何实现强弱结合型事务。文章还提供了分布式事务的标准面试答案,并推荐了《尼恩Java面试宝典PDF》等资源,帮助大家在面试中脱颖而出。
|
3月前
|
消息中间件 中间件 关系型数据库
常用的分布式事务解决方案(四)
常用的分布式事务解决方案(四)
|
3月前
常用的分布式事务解决方案(二)
常用的分布式事务解决方案(二)
|
2月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
24天前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
70 5
|
28天前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
61 8