把脉分布式事务的模型、协议和方案

2023-04-18  本文已影响0人  Java程序员YY

在当前的技术发展阶段,不同的业务场景对一致性、可靠性、易用性、性能等要求不同,应用架构可以根据实际场景的需求,灵活选择合适的分布式事务解决方案。行业中把分布式事务解决方案分为刚性事务方案和柔性事务方案这两大类。

就刚性事务这个范畴,The Open Group 的组织提出的 X/Open Distributed Transaction Processing (DTP) Model ,已经成为事实上的事务模型组件的行为标准,解决了不少分布式事务的问题。但随着技术以及业务场景的演进发展,其缺点和局限性也越来越明显,伴随着 BASE 理论的提出,诸多柔性事务方案(如可靠消息、最大努力通知、AT、TCC、Saga)诞生,满足了各类业务场景的差异化需求。

一、XA(2PC) 规范和 DTP 模型介绍

1.1、 前奏- 2 阶段提交协议(2PC)

在开始介绍 DTP 模型之前,需要先了解 两阶段提交协议。2 阶段提交协议(The two-phase commit protocol)简称 2PC,2PC 是为了要保证分布式事务的原子性,此协议中有【协调者】和【参与者】两个角色,整个处理过程包含两个阶段:投票阶段 和 提交阶段:

  1. 投票阶段
  1. 提交阶段

整个过程要么成功,要么失败回滚,对外部来说,该事务的状态变化是原子的。要达到这个效果,对 2 个阶段的处理是有约束的:

1.2、 DTP 模型介绍

X/Open 组织定义的分布式事务的处理(DTP)模型由以下几个元素组成:

在 DTP 中,基本组成需要涵盖 AP、TM、RMs(不需要 CRM、CP 也是可以的),如下图所示

1.3 XA 规范介绍

XA 规范是 X/Open 组织提出的分布式事务处理 (DTP)的接口规范(所以也常看到被叫做 XA 接口 ),描述了 RM 要支持事务性访问所必需做的事情,XA 接口在 TM 与 RM 之间提供双向通信。它是 TM、RM 这两个 DTP 软件组件之间的系统级别接口,而不是应用程序开发者对其进行编码的普通应用程序接口。

重要提示:为什么有的资料把 XA 叫做 2PC ,把 XA 和 2PC 描述为同一个事物。DTP、XA、2PC 之间是什么关系?可以这么理解,DTP 模型定义 TM 和 RM 之间通讯的接口规范叫 XA,XA 规范使用 2PC 来保证事务中的所有资源全部提交或全部回滚。

二、MySQL 对 XA 规范的支持

DTP 模型定义了 TM 和 RM 之间通讯的 XA 规范,而 DB 在 DTP 模型中担任 RM 的角色。为了让上层应用可以便捷的使用分布式事务,许多关系型 DB 都实现了 X/Open 的 XA 规范。MySQl 从 5.x 开始支持 XA 规范,但仅 innodb 存储引擎支持 。

2.1 MySQL XA 命令介绍

这里列举出 MySQL XA 的命令集合,先有个初步认识;看完后边状态流转和示例后可加深印象。

命令 解释
XA START xid 开启一个事务,并将事务置于 ACTIVE 状态,此后执行的 SQL 语句都将置于该事务中
XA END xid 将事务置于 IDLE 状态,表示事务内的 SQL 操作完成
XA PREPARE xid 实现事务提交的准备工作,事务状态置于 PREPARED 状态。事务如果无法完成提交前的准备操作,该语句会执行失败
XA COMMIT xid 事务最终提交,完成持久化
XA ROLLBACK xid 事务回滚终止
XA RECOVER 查看 MySQL 中存在的 PREPARED 状态的 xa 事务

2.2 事务状态转换

XA 事务可以一步提交(非本篇关注点),本篇重点是两阶段提交。对于两阶段提交来说,要在 prepare 之后等其他 RM 反馈结果。状态转换流程如下图所示:

2.3、一个简单的示例:

// 第一阶段
// 1.1 通过 XA START 和 XA END 来包裹 用户业务SQL
mysql> XA START 'transfer_money';
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE account SET money = money -100 where id = 1 ;
Query OK, 1 row affected (0.00 sec)

mysql> XA END 'transfer_money';
Query OK, 0 rows affected (0.00 sec)

// 1.2 通过 XA PREPARE 通知 RM 一阶段就绪
mysql> XA PREPARE 'transfer_money';
Query OK, 0 rows affected (0.00 sec)

// 第二阶段,通过 XA COMMIT 完成二阶段的提交
mysql>
mysql> XA COMMIT 'transfer_money';
Query OK, 0 rows affected (0.00 sec)

2.4、MySQL XA 事务异常处理

XA 事务 prepare 成功后,若不提交就相当于是长事务了,要尽快提交,否则会带来很多问题。但 DB 或应用出错导致 XA 事务未能全部提交的情况也无法避免,这些残存 XA 事务可人工处理,执行 xa recover 查看未提交 XA 事务,选择对应的进行 rollback 或 commit。

2.5、java 如何使用 MySQL XA 方案

java 项目中若使用 MySQL XA 方案,有 JTA(Atomikos 框架提供支持) 和 Seata 的 XA 模式,这两种实现方式。

*   非常有名的分布式事务开源框架 atomikos 提供了开源免费的 JTA/XA 实现(也有 TCC 机制的商业付费版实现)。接入简单,只需引入对应 jar 包,无需额外的服务。但 JTA 方案仅适用于单体架构多数据源时实现分布式事务

三、XA(2PC) 的优缺点

2PC 是 XA 规范用于在全局事务中协调多个资源的机制。2PC 遵循 OSI(Open System Interconnection,开放系统互联)/DTP 标准,但实际它比标准本身还要早若干年出现。

网络中关于 2PC 的描述有的是仅指 2PC 算法本身,而有的则用 2PC 来代指 XA;依笔者目前有限的认知来看,网络资料中大多关于 2PC 缺点的描述,是特指 XA(即强一致方案下的 2PC)。因为目前其他主流的如 TCC、AT 等模式也属于是 2PC 的变种,但其出现是为了解决了 XA 已知的诸多问题,在阅读资料时需根据上下文来辨识。

3.1 XA(2PC) 的优点

3.2 XA(2PC) 的缺点

四、3 阶段提交(3PC)

3 阶段提交在维基百科的描述是这样:

三阶段提交(英语:Three-phase commit),也叫三阶段提交协议(英语:Three-phase commit protocol),是在电脑网络及数据库的范畴下,令一个分布式系统内的所有节点能够执行事务的提交的一种分布式算法。三阶段提交是为了解决两阶段提交协议的缺点而设计的。

与两阶段提交不同的是,三阶段提交是一种“非阻塞”的协议。三阶段提交在两阶段提交的第一阶段与第二阶段之间插入了一个准备阶段,令原先在两阶段提交中,参与者在投票之后,由于协调者发生崩溃或错误,而导致参与者处于无法知晓是否提交或者中止的“不确定状态”所产生的可能相当长的延时的问题[1]得以解决。

简单来说就是把 2PC 中的第 1 个阶段拆分成先询问所有参与者是否可以执行事务-得到一致的同意回复后才执行预提交并锁资源,最后真正提交。

4.1、阶段 1:CanCommit

  1. 协调者向各参与者发送 CanCommit 的请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应

  2. 参与者收到 CanCommit 请求后,正常情况下,如果自身认为可以顺利执行事务,那么会反馈 Yes 响应,并进入预备状态,否则反馈 No

4.2、阶段 2:PreCommit

这个环节根据阶段 1 的参与者的反馈不同,而执行不同的逻辑:

1)如果任意一个参与者在阶段 1 向协调者反馈了 No 响应,或者协调者等待参与者反馈超时,那么就会中断事务,中断执行逻辑如下:

  1. 协调者向所有参与者发送 abort 请求
  2. 参与者无论是收到来自协调者的 abort 请求,还是等待超时,都执行事务中断

2)另一种情况,如果协调者接收到各参与者反馈都是 Yes,那么才执行事务预提交,执行逻辑如下:

  1. 协调者向各参与者发送 preCommit 请求,并进入 prepared 阶段

  2. 各参与者接收到 preCommit 请求后执行事务操作,并将 Undo 和 Redo 信息记录到事务日记中,但事务不提交

  3. 如果各参与者都成功执行了事务操作,那么反馈给协调者 Ack 响应,同时等待最终指令,提交 commit 或者终止 abort

4.3、阶段 3:doCommit

这个环节根据阶段 2 参与者不同反馈,而执行不同的逻辑:

1)假设协调者正常工作,并且有任意一个参与者在阶段 2 反馈 No,或者在等待参与者的反馈超时后,都会主动中断事务

2)假设协调者正常工作,接收到了所有参与者的 ack 响应,那么它将从预提交阶段进入提交状态

4.4、3PC 的核心理念

3PC 的核心理念是:在询问的时候并不锁定资源,除非所有人都同意了,才开始锁资源。怎么展开来理解呢?

  1. 在锁定资源之前通过询问的方式执行一次资源锁定前的预检,可以减少非必要资源锁定

  2. 通过两次确认来保证在进入最后提交阶段时各参与节点的状态是一致的

  3. 避免无限等待,同时在协调者和参与者中都引入超时机制

  4. 在参与者完成 PreCommit 后,即使遇到与协调者交互超时的问题,仍可自主的继续 Commit。因为完成 PreCommit 后也意味着大家其实都在 CanCommit 阶段同意修改了,此时采取 Commit 极大的概率不会错的。而 2 阶段提交中参与者在 1 阶段后是不知所措的(不知道其他参与者怎么反馈,不知道协调者的决议是什么),而且不知所错的状态可能持续相当长的时间。

4.5、探讨 3PC 中的异常处理

在协调者和参与者中都引入超时机制,超时后自醒,主动执行自认为合理的下一步逻辑(回滚或者提交),避免无上限的挂起:

1)canCommit 阶段

2)pre commit 阶段

3)do commit 阶段

4.6、3PC 的优缺点

2PC 和 3PC ,它们是在分布式数据库管理系统 (Distributed Database Management System,DDBMS) 中管理如何提交或中止数据库事务的最流行算法。2PC 协议是一个阻塞的两阶段提交协议,3PC 协议是一种非阻塞的三阶段提交协议。通过询问避免了资源的无效占用;协调者和参与者各自有超时自醒处理,避免长时间资源占用不释放,提高资源利用率。消除了 2PC 协议的阻塞问题,但是,3PC 协议在网络出现分段的情况下不会恢复。因此,Keidar 和 Dolev (1998) 建议使用增强型三阶段提交 (E3PC) 协议来消除此问题。E3PC 协议需要至少三个往返才能完成,这将有很长的延迟才能完成每笔交易。

所以,无论是 3PC 还是 E3PC,虽然解决了一些问题,但遗留问题以及复杂度的提升也导致性价比不高,估计在实际应用中的效果并不好,所以目前普遍使用的依然是 XA(2PC) ;对 3PC 的学习都是理论(缺产品),通过对 3PC 理论学习探讨,来理解经典 XA(2PC) 的优缺点,并作为后续学习柔性事务方案的理论借鉴。

补充:有些网络资料描述 XA 包括 2PC 和 3PC,但从维基百科的信息来看,对 XA 的描述中并未提及 3PC,且 3PC 的描述中也未看到 XA。而且 3PC 没有让人熟悉的产品实现,后续内容就不再与其他可实施落地的方案一起讨论了。

五、柔性事务介绍

XA(2PC)由数据库做为参与者管理事务资源,提供分布式事务的处理支持,所以可以保障从任意视角对数据的访问都能有效隔离,满足全局数据一致性,被称作刚性事务。

刚性事务属于 CAP 理论中的 CP 组合,会有性能上限,无法满足高并发场景的需求。基于 BASE 理论,柔性事务方案被提出用于保证事务数据的最终一致性。柔性事务本质是对 XA 协议的妥协,它通过降低强一致性要求,从而降低数据库资源锁定时间,提升可用性,允许有中间状态,要求最终一致性,也就是 AP 组合。柔性事务分为通知型和补偿型:

六、通知型事务

6.1、可靠消息

可靠消息方案是指当事务发起方(消息发送者)执行完成本地事务后并发出一条消息,事务参与方(消息接收者)一定能够接收消息并处理事务成功。

此方案强调的是一旦消息发给事务参与方,则最终事务要达到一致。这其中有两个关键问题:

  1. 本地事务与消息发送的原子性问题
  1. 事务参与方接收消息的可靠性问题

可靠消息方案适合执行周期长且实时性要求不高的场景。引入消息机制后,原先同步的事务操作变为基于消息写/读的异步操作,避免了同步阻塞,也实现了服务间的解耦。一般有基于本地消息表和基于消息中间件这两种实现式。

1)基于本地消息表

本地消息表核心思路是:将分布式事务拆分成本地事务进行处理

优点:从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对 MQ 中间件特性的依赖,方案轻量,容易实现。

缺点:需设计 DB 消息表,同时还需要一个后台任务,不断扫描本地消息。导致消息的处理和业务逻辑耦合额外增加业务方的负担。

2)基于 MQ 事务消息

为了能解决本地消息表方案的这个缺点,同时又不和业务耦合,RocketMQ 提供了“事务消息”的能力,可以理解为将本地消息表移动到了 MQ 内部。

事务消息发送步骤如下:

  1. 发送方将半事务消息发送至 RocketMQ 服务端

  2. 服务端将消息持久化成功之后,向发送方返回 ACK 确认消息已经发送成功,此时消息为半事务消息

  3. 发送方开始执行本地事务逻辑

  4. 发送方根据本地事务执行结果向服务端提交二次确认(Commit 或是 Rollback),服务端收到 Commit 状态则将半事务消息标记为可投递,订阅方最终将收到该消息;服务端收到 Rollback 状态则删除半事务消息,订阅方将不会接受该消息

事务消息回查步骤如下:

  1. 在断网或者是应用重启的特殊情况下,上述发送步骤的步骤 4 提交的二次确认最终未到达服务端,经过固定时间后服务端将对该消息发起消息回查

  2. 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果

  3. 发送方根据检查得到的本地事务的最终状态再次提交二次确认,服务端仍按照发送步骤的步骤 4 对半事务消息进行操作

6.2、最大努力通知

最大努力通知型的目标是 事务发起方尽量将业务处理结果通知到参与方。适用于一些最终一致性时间敏感度低,且参与方的处理结果不影响发起方的处理结果的这类通知类的业务场景。如短信供应商的回执通知:

最大努力通知型的实现方案,一般符合以下两个特点:

  1. 消息重复通知
  1. 定期校对

6.3 最大努力通知 VS 可靠消息

1)可靠性的保障方不同

2)两者的业务应用场景不同

3)技术解决方向不同

七、补偿型事务模式

AT、TCC、Saga 都是 补偿型的事务模式,应用层面的来提供分布式事务能力,不用依赖数据库对协议的支持,完全剥离了分布式事务方案对数据库在协议支持上的要求(留意下图中 RM 已从 DB 层移至应用层)。

AT、TCC、Saga 也是两阶段处理。有资料中描述 AT、TCC、Saga 属于 2PC 变种,也有一些文章中用 2PC 来代指 XA,在阅读资料时需根据上下文来辨识。

上一篇下一篇

猜你喜欢

热点阅读