Fescar 分布式事物中间件

1、前世今生

1.1、发展历史

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

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

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

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

1.2、背景

阿里是国内最早一批进行应用分布式(微服务化)改造的企业,(这是必然,一般的号称自己是互联网企业的,其实流量不是很大,应用系统更新重构了几次了,也没有遇到过多大的量,正所谓在其位谋其政,没有必要费人费力的去做这些分布式高大上的东西,自然懂的人也就相对少些)。分布式事物是互联网交易场景面临的关键问题之一,也是个世界级(一点都不夸张)的难点,不同于一般的搜索、社交、应用分析等,电子商务、支付是典型的交易场景,数据的错误会带来非常严重的后果,不管是大是小,最终都是一个结果,长款或者短款,短款的话公司就要面临亏损,当然出问题的人的日子也不好过了。

对数据的一致性与可用性有很高的要求,互联网环境带来了海量的数据容量、数据库连接数与访问量,单一数据库节点无法应对,数据库的性能其实成为了系统的瓶颈,通过业务的垂直拆分和数据的水平拆分,实现数据库能力的线性扩展,其实就是分库分表的方式,将数据存储在多个数据库节点,利用分布式数据库平台解决数据库瓶颈的问题。但是这样一来,本来数据库的事物只是在一台服务上的,现在涉及到多个数据库,面临分布式事物处理的问题。

1.3、三个因素

分布式事务解决方案面临应用灵活性、数据一致性、性能三者的挑战。目前已有多种成熟方案,每种方案都是对这三个方面做出的取舍。

  1. 应用灵活性:应用访问数据的方式是否需要修改,以及修改的程度。
  2. 一致性:数据是强一致,还是最终一致的(允许中间不一致的状态)。
  3. 系统性能:分布式事务对整体性能的影响。

2、现有的分布式处理方案

现有成熟的方案中包括XA两阶段提交、三阶段提交、TCC、各种补偿模式(可靠消息模式)。XA两阶段、三阶段提交属于强一致事物,TCC、可靠消息模式属于柔性事物。

2.1、两阶段

两阶段提交协议把分布式事事物分为两个阶段

2.1.1、准备阶段

协调者向参与者发起指令,参与者评估自己的状态,如果参与者评估指令可以完成,则会写redo 或者 undo 日志(Write-Ahead Log的一种)然后锁定资源,执行操作,但是并不提交

2.1.2、提交阶段

如果每个参与者明确返回准备成功,也就是预留资源和执行操作成功,则协调者向参与者发起提交指令,参与者提交资源变更的事物,释放锁定的资源;如果任何一个参与者返回明确返回准备失败,也就是预留资源或者执行操作失败,则协调者向参与者发起终止指令,参与者取消已经变更的事物,执行undo 日志,释放锁定的资源

 

如图:

两阶段提交协议在准备阶段锁定资源,这是一个重量级的操作,能保证强一致性,但是实现起来复杂、成本较高、不够灵活,更重要的是它有如下问题:

  1. 阻塞,
    对于上图的操作,任何一次指令,都必须要接收到明确的的响应,才会继续下一步骤,否则就会一直阻塞,并且占用资源不释放;
    对于已经锁定的资源,其他进程处理被锁定的资源时,处于等待状态,直到资源被释放,事物执行时间的延长,以为着锁冲突的概率增加,可能会出现事物积压甚至死锁,性能严重下滑。
  2. 单点故障,
    协调者宕机,参与者没有协调指挥者,则会一直阻塞,虽然可以通过重新选举新的协调者,但是当协调者发送一个提交指令后宕机,只有一个参与者接收到指令,接收后参与者也宕机了,则新选举的协调者无法处理这种情况。
  3. 脑裂,
    协调者发送提交指令,发生局部网络异常或者有些参与者发生故障,有些参与者接收到提交指令,有些参与者没有接收到提交指令,从而导致分布式系统中出现数据不一致。

个人认为导致使用性不够好的原因,一个,需要数据库的支持,且不能够进行干预,只能数据库层面上进行完善,第二个,第一阶段长时间占中资源,在互联网高并发,海量数据环境下,不实用

2.2、三阶段

三阶段是两阶段的改进版本,它通过超时机制解决了阻塞问题,并且新增一个询问阶段

2.2.1、询问阶段

协调者询问参与者是否可以完成指令,参与者只需要回答是或者而不是,而不需要真正的准备操作,这个阶段超时会终止。

2.2.2、准备阶段

如果在询问阶段都返回了可以执行操作,则协调者向参与者发送预执行指令,燃火参与者写 redo 和 undo 日志,执行操作但是不提交操作;如果询问阶段任意一个参与者返回不能执行操作,则协调者向参与者发送终止请求。

2.2.3、提交阶段

如果每个参与者在准备阶段明确返回成功,则协调者向参与者发起提交指令,参与者提交资源变更事物,释放锁定资源,如果任意参与方返回失败,则协调者向参与者发起终止指令,参与者取消已经变更的事物,执行undo日志,释放锁定资源。与两阶段的处理一致

 

如图:

三阶段提交协议与两阶段主要有两个不同点

  1. 增加了一个询问阶段,询问阶段是尽可能的早发现无法执行操作而需要终止的行文,但是它并不能发现所有的这种行为,只会减少这种情况的发生。
  2. 在准备阶段以后,协调者和参与者的执行任务中都增加了超时,一旦超时,则协调者和参与者会继续提交事务,默认成功,这个决定是根据概率统计之后成功的正确性最大来确定的。

虽然可以解决阻塞和永远锁定资源的问题,但是一旦超时了,还是有可能会发生数据的不一致,只不过这种情况很少见。依然会有在超时时间内占用资源的问题。

2.3、TCC

不管是两阶段还是三阶段,一个是实现复杂,长时间的锁定资源,往往是没有必要的,事物资源的管理器是数据库本身,应用层无法插手,这样就导致,基于XA 的应用性能往往比较差,这是不符合微服务架构的。

tcc协议,将一个任务拆分成try、confirm、cancel 三个不收,正常流程下会先执行try,如果执行没有问题,则在执行confirm,如果执行过程中除了问题,则执行cancel,从正常的流程上讲,这仍然是一个两阶段提交协议,但是在执行出现问题的时候,可以进行自我修复,最终达到一致的状态,目前信贷服务中,使用的是算是简版的TCC ,因为没有TCC 管理器,是使用一张数据库表实现并且配合何种查询,补偿、定期校对的补偿方式,达到最终的数据一致,目前还没有出现漏单,也就是数据对不上的情况。

2.4、可靠消息模式

2018-01-01-MSG-DT.png

 

说明:

  • 业务处理服务在业务事务提交前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送。
  • 业务处理服务在业务事务提交后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才真正发送消息。
  • 业务处理服务在业务事务回滚后,向实时消息服务取消发送。
  • 消息状态确认系统定期找到未确认发送或回滚发送的消息,向业务处理服务询问消息状态,业务处理服务根据消息ID或消息内容确定该消息是否有效。

通过消息进行事务异步的方式,可以保证业务数据操作和消息的发送同时执行成功或失败,保持了事务的最终一致性。

采用可靠消息的方式,在两个事务间实现分布式事务时,可以很好地满足事务最终一致性以及事务的回滚,但如果一个事务上下文中超过两个事务操作后,需要开发人员实现整个事务流程的操作日志的记录、每个事务分支的回滚以及整个流程的准确调度。

2.5、侵入性

2.5.1、侵入方案

因为分布式事务这个技术问题的制约,要求应用在业务层面进行设计和改造。这种设计和改造往往会给应用带来很高的研发和维护成本。我们希望把分布式事务问题在 中间件 这个层次解决掉,不要求应用在业务层面做额外的工作。

2.5.2、非侵入方案

业务代码不需要改动,只是数据库层面的,

小结,现有的解决方案,两阶段和三阶段都属于无侵入方式,是数据库层面的,应用无修改,可以保证数据的一致性,但复杂度和性能不理想却又不是理想的解决方案,TCC 属于侵入的方式,应用需要改动,可以保证数据的一致性,这种设计和改造往往会给应用带来很高的研发和维护成本,性能还是有些影响,

3、fescar 的设计方案考量

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

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

  • 对业务无侵入:类似于XA的解决方案
  • 高性能: 引入分布式事务的保障,必然会有额外的开销,引起性能的下降。我们希望把分布式事务引入的性能损耗降到非常低的水平,让应用不因为分布式事务的引入导致业务的可用性受影响。

对业务无侵入的只有基于 XA 的方案,但应用 XA 方案存在 3 个方面的问题:

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

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

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

4、原理和设计

既然设计无无侵入的方案,已XA 的方案来思考,可以在XA的基础上进行演进,解决掉XA 具有的问题?

4.1、定义一个分布式事物

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

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

FESCAR Model

 

 

  • 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 下管辖的全部分支事务完成提交或回滚请求。

     

Architecture

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

4.2、与XA的区别

结构层次:

RM in Architecture

 

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

 

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

 

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

两阶段提交

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

XA-2PC

 

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

 

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

FESCAR-2PC

 

  • 分支事务中数据的 本地锁 由本地事务管理,在分支事务 Phase1 结束时释放。
  • 同时,随着本地事务结束,连接 也得以释放。
  • 分支事务中数据的 全局锁 在事务协调器侧管理,在决议 Phase2 全局提交时,全局锁马上可以释放。只有在决议全局回滚的情况下,全局锁 才被持有至分支的 Phase2 结束。
     

这个设计,极大地减少了分支事务对资源(数据和连接)的锁定时间,给整体并发和吞吐的提升提供了基础。

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

4.3、分支事物如何提交和回滚

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

Data Source Proxy

 

4.3.1、Phase1:

 

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

 

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

Branch Transaction with UNDO LOG

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

4.3.2、Phase2:

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

Global Commit

 

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

Global Rollback

4.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:不支持

4.5、隔离性

 

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

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

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

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

Isolation

注意,本地事物的级别一般我们都会采用默认的级别就是 可重复读 的级别,全局事务-读未提交,当然也可以通过修改sql语句添加 for update 将读事物的级别提高到读已提交的级别

4.6、适用场景

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

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

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

 

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

4.6.1、分支的基本行为模式

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

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

How does RM talk to TC

4.6.2、AT模式分支的行为模式

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

AT branch

4.6.3、MT模式分支的行为模式

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

MT branch

其实就是一个TCC 的模式,只不过可以将众多非事物性的资源纳入到全局事务中进行管理,是AT模式的以一种补充。

4.6.4、混合模式

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

4.6.5、应用场景的愿景

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

Roadmap of Transaction Mode

5、其他

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 存储的方案。

参考:https://github.com/alibaba/fescar/wiki/%E6%A6%82%E8%A7%88

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值