在以后的技术倒退阶段,不同的业务场景对一致性、可靠性、易用性、性能等要求不同,利用架构能够依据理论场景的需要,灵便抉择适合的分布式事务解决方案。行业中把分布式事务解决方案分为刚性事务计划和柔性事务计划这两大类。
就刚性事务这个领域,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 是为了要保障分布式事务的原子性,此协定中有【协调者】和【参与者】两个角色,整个处理过程蕴含两个阶段:投票阶段 和 提交阶段:
- 投票阶段
- 协调者发送事务准备(Prepare)申请到所有的参加节点,并询问它们是否能够提交。
- 提交阶段
- 根据第 1 阶段返回的后果,决定事务最终是提交(Commit)还是放弃(Rollback)。
- 如果所有的参加节点都回复 Yes,那么协调者在第 2 阶段收回提交(Commit)申请。
- 如果任一参加节点回复“No”,那么协调者在第 2 阶段收回回滚(Rollback)申请。
整个过程要么胜利,要么失败回滚,对外部来说,该事务的状态变动是原子的。要达到这个成果,对 2 个阶段的解决是有束缚的:
- 在第 1 阶段,当事务的参与者回复“Yes”的时候,对于以后事务,这个参与者肯定是可能平安提交的
- 在第 2 阶段,当协调者基于参与者的投票,做出提交或者回滚的决定后,这个决定是不能够撤销的
1.2、DTP 模型介绍
X/Open 组织定义的分布式事务的解决(DTP)模型由以下几个元素组成:
-
AP(Application 应用程序)
- 用于定义事务边界(即定义事务的开始和完结),并且在事务边界内对资源进行操作
-
TM(Transaction Manager 事务管理器)
- 负责调配事务惟一标识,监控事务的执行进度,并负责事务的提交、回滚等
-
RM(Resource Manager 资源管理器)
- 如数据库、文件系统等,并提供拜访资源的形式
-
CRM(Communication Resource Manager 通信资源管理器)
- 管制一个 TM 域 (TM domain) 内或者跨 TM 域的分布式应用之间的通信
-
CP(Communication Protocol 通信协议)
- 提供 CRM 提供的分布式应用节点之间的底层通信服务
在 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 反馈后果。状态转换流程如下图所示:
- XA 事务开启时多了一个全局事务号,完结时多了一个 end 动作 和 prepare 动作
- XA START, 开启一个分布式事务,须要指定分布式事务号
- XA END,在外部仅是一个状态变动,申明以后 XA 事务完结,不容许追加新的 sql 语句,无其它作用,业界有人提出 XA 事务框架去掉这一步,缩小一次网络交互,进步性能
- XA PREPARE,MySQL 5.7 版本此指令才写 binlog 和 redo log,预提交事务,并将分布式事务信息保留到全局内存构造,让其它连贯能够查问、回滚、提交
- XA COMMIT,真正提交事务,批改事务状态,开释锁资源。如果实例上 XA PREPARE 曾经胜利,那么它的 XA COMMIT 肯定能胜利
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 模式,这两种实现形式。
-
JTA(Atomikos)
- JTA(Java Transaction API)是 java 依据 XA 标准提供的事务处理规范,目标是对立一套 API 简化学习,JTA 和它的同胞 JTS(Java Transaction Service),为 J2EE 平台提供了分布式事务服务
- JTA 的指标是屏蔽底层事务资源,使利用能够通明的形式参入到事务处理中(Seata 的也相似)
* 十分有名的分布式事务开源框架 atomikos 提供了开源收费的 JTA/XA 实现(也有 TCC 机制的商业付费版实现)。接入简略,只需引入对应 jar 包,无需额定的服务。但 JTA 计划仅实用于单体架构多数据源时实现分布式事务
-
Seata XA 模式
- Seata XA 模式可应答多个微服务间的分布式事务,应用时除了须要引入依赖 Jar,还须要启动 Seata Server 这个额定的分布式事务协调者服务
三、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) 的长处
- 业务无侵入:XA 模式将是业务无侵入的,不给利用设计和开发带来额外负担
- 数据库的反对宽泛:XA 协定被支流关系型数据库广泛支持,不须要额定的适配即可应用
3.2 XA(2PC) 的毛病
-
数据锁定
- 应用 XA 事务时,数据在整个事务的二阶段解决完结前都被锁定,数据的读写都恪守 DB 隔离级别的定义
-
协定阻塞
- XA prepare 后,分支事务进入阻塞阶段,收到 XA commit 或 XA rollback 前必须阻塞期待。但协定阻塞期待的背地问题理论是资源的锁期待,如果一个 RM 收不到分支事务二阶段的指令,那么它锁定的数据,将始终被锁定,其余事务拜访此资源时就不得不也阻塞期待,可能呈现死锁。这是 XA 的外围痛点,这个痛点中协调者的可用性是个要害,Seata 次要是解决了失联问题,并通过减少自解锁机制来解决这个问题(后续文章补充)
-
性能差
- 因为以上两点,事务 2 阶段的处理过程,减少单个事务的 RT;并发事务数据的锁抵触,升高吞吐
四、3 阶段提交(3PC)
3 阶段提交在维基百科的形容是这样:
三阶段提交(英语:Three-phase commit),也叫三阶段提交协定(英语:Three-phase commit protocol),是在电脑网络及数据库的领域下,令一个分布式系统内的所有节点可能执行事务的提交的一种分布式算法。三阶段提交是为了解决两阶段提交协定的毛病而设计的。
与两阶段提交不同的是,三阶段提交是一种“非阻塞”的协定。三阶段提交在两阶段提交的第一阶段与第二阶段之间插入了一个筹备阶段,令原先在两阶段提交中,参与者在投票之后,因为协调者产生解体或谬误,而导致参与者处于无奈通晓是否提交或者停止的“不确定状态”所产生的可能相当长的延时的问题 [1] 得以解决。
简略来说就是把 2PC 中的第 1 个阶段拆分成先询问所有参与者是否能够执行事务 - 失去统一的批准回复后才执行预提交并锁资源,最初真正提交。
4.1、阶段 1:CanCommit
- 协调者向各参与者发送 CanCommit 的申请,询问是否能够执行事务提交操作,并开始期待各参与者的响应
- 参与者收到 CanCommit 申请后,失常状况下,如果本身认为能够顺利执行事务,那么会反馈 Yes 响应,并进入准备状态,否则反馈 No
4.2、阶段 2:PreCommit
这个环节依据阶段 1 的参与者的反馈不同,而执行不同的逻辑:
1)如果任意一个参与者在阶段 1 向协调者反馈了 No 响应,或者协调者期待参与者反馈超时,那么就会中断事务,中断执行逻辑如下:
- 协调者向所有参与者发送 abort 申请
- 参与者无论是收到来自协调者的 abort 申请,还是期待超时,都执行事务中断
2)另一种状况,如果协调者接管到各参与者反馈都是 Yes,那么才执行事务预提交,执行逻辑如下:
- 协调者向各参与者发送 preCommit 申请,并进入 prepared 阶段
- 各参与者接管到 preCommit 申请后执行事务操作,并将 Undo 和 Redo 信息记录到事务日记中,但事务不提交
- 如果各参与者都胜利执行了事务操作,那么反馈给协调者 Ack 响应,同时期待最终指令,提交 commit 或者终止 abort
4.3、阶段 3:doCommit
这个环节依据阶段 2 参与者不同反馈,而执行不同的逻辑:
1)假如协调者失常工作,并且有任意一个参与者在阶段 2 反馈 No,或者在期待参与者的反馈超时后,都会被动中断事务
- 协调者向所有参与者节点发送 abort 申请
- 参与者接管到 abort 申请后,利用 undo 日志执行事务回滚,并在实现事务回滚后开释占用的资源后,向协调者发送 ack 信息,反馈事务回滚后果
- 协调者接管到所有参与者反馈的 ACK 音讯之后,实现事务的中断
2)假如协调者失常工作,接管到了所有参与者的 ack 响应,那么它将从预提交阶段进入提交状态
- 协调者向所有参与者发送 doCommit 申请
- 参与者收到 doCommit 申请后,正式提交事务,并在实现事务提交后开释占用的资源,向协调者发送 ACK 信息,反馈事务提交后果
- 协调者接管到所有参与者 ack 信息,整个事务实现
4.4、3PC 的核心理念
3PC 的核心理念是:在询问的时候并不锁定资源,除非所有人都批准了,才开始锁资源。怎么开展来了解呢?
- 在锁定资源之前通过询问的形式执行一次资源锁定前的预检,能够缩小非必要资源锁定
- 通过两次确认来保障在进入最初提交阶段时各参加节点的状态是统一的
- 防止有限期待,同时在协调者和参与者中都引入超时机制
- 在参与者实现 PreCommit 后,即便遇到与协调者交互超时的问题,仍可自主的持续 Commit。因为实现 PreCommit 后也意味着大家其实都在 CanCommit 阶段批准批改了,此时采取 Commit 极大的概率不会错的。而 2 阶段提交中参与者在 1 阶段后是手足无措的(不晓得其余参与者怎么反馈,不晓得协调者的决定是什么),而且不知所错的状态可能继续相当长的工夫。
4.5、探讨 3PC 中的异样解决
在协调者和参与者中都引入超时机制,超时后自醒,被动执行自认为正当的下一步逻辑(回滚或者提交),防止无下限的挂起:
- 协调者,遇到超时就给参与者发 abort 指令,中断整个事务
- 参与者,在收到 can commit 后,执行 pre commit 之前,超时自醒后间接中断 RM 的分支事务
- 参与者,在收到 pre commit 指令后,期待下一步的 do commit 指令超时,间接提交事务(大概率是正确的抉择,但如果是谬误的抉择就导致了数据的不统一)
1)canCommit 阶段
- 局部参与者收到 canCommit 指令后,间接反馈不能开始事务,协调者向所有参与者发送 abort 申请。
- 协调者期待反馈超时,向所有参与者发送 abort 申请。
- 参与者预检没问题,但始终无奈接管到下一步的的指令(反馈 can commit 超时 或接管协调者的下一步指令超时),自省中断事务的中断
- 参与者收到来自协调者的 abort 申请之后执行事务中断
2)pre commit 阶段
- 协调者收到参与者反馈 PreCommit 异样,向所有参与者发送 abort 申请,执行回滚
- 协调者若期待参与者的 PreCommit 反馈超时,向所有参与者发送 abort 申请,执行回滚
3)do commit 阶段
- 参与者收到 PreCommit 指令并失常执行事务,给协调者反馈 preCommit 实现后,实践上是要期待下一步的 doCommit 指令后才提交事务;然而如果未能等到下一步的 doCommit 指令超时了,会自主提交事务。因为询问阶段是统一通过的,执行到这个阶段整个事务胜利的概率曾经很高了
- 局部参与者执行 preCommit 异样,局部参与者执行 preCommit 失常,但此时协调者挂了,那么通过参与者的超时自醒机制,就呈现局部参与者提交,局部参与者回滚,呈现数据不统一。
- 另外一种状况,协调者发送了 abort 指令,参与者超时未收到指令就提交了事务,其余参与者收到了协调者发送的 abort 指令后执行了回滚,也会呈现数据不统一。
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 组合。柔性事务分为告诉型和弥补型:
- 告诉型事务都是异步的,蕴含有:可靠消息、最大致力告诉两种
- 弥补型事务都是同步的,蕴含有:AT、TCC、Saga
六、告诉型事务
6.1、可靠消息
可靠消息计划是指当事务发起方(音讯发送者)执行实现本地事务后并收回一条音讯,事务参与方 (音讯接收者) 肯定可能接管音讯并处理事务胜利。
此计划强调的是一旦音讯发给事务参与方,则最终事务要达到统一。这其中有两个关键问题:
- 本地事务与音讯发送的原子性问题
- 若事务发起方在本地事务执行胜利,则音讯必须收回(能够是立刻发送,也能够是异步发送),否则就无音讯
- 事务参与方接管音讯的可靠性问题
- 参与方必须可能从 MQ 接管到音讯,如果接管音讯失败能够反复接管音讯。
- 参与方要解决音讯反复生产的问题(生产解决的幂等性)。
可靠消息计划适宜执行周期长且实时性要求不高的场景。引入音讯机制后,原先同步的事务操作变为基于音讯写 / 读的异步操作,防止了同步阻塞,也实现了服务间的解耦。个别有基于本地音讯表和基于消息中间件这两种实现式。
1)基于本地音讯表
本地音讯表外围思路是:将分布式事务拆分老本地事务进行解决
- 在音讯发送者一端 额定新建事务音讯表
- 音讯发送者一端在同一个本地事务中解决业务和写音讯表操作
- 定时工作轮询事务音讯表的待发送的数据,确保发送给 MQ
- 在音讯消费者一端生产 MQ 音讯,若生产失败则重试生产,最终达到发送者和接收者两端数据的最终一致性
长处:从利用设计开发的角度实现了音讯数据的可靠性,音讯数据的可靠性不依赖于消息中间件,弱化了对 MQ 中间件个性的依赖,计划轻量,容易实现。
毛病:需设计 DB 音讯表,同时还须要一个后台任务,一直扫描本地音讯。导致音讯的解决和业务逻辑耦合额定减少业务方的累赘。
2)基于 MQ 事务音讯
为了能解决本地音讯表计划的这个毛病,同时又不和业务耦合,RocketMQ 提供了“事务音讯”的能力,能够了解为将本地音讯表挪动到了 MQ 外部。
事务音讯发送步骤如下:
- 发送方将半事务音讯发送至 RocketMQ 服务端
- 服务端将音讯长久化胜利之后,向发送方返回 ACK 确认音讯曾经发送胜利,此时音讯为半事务音讯
- 发送方开始执行本地事务逻辑
- 发送方依据本地事务执行后果向服务端提交二次确认(Commit 或是 Rollback),服务端收到 Commit 状态则将半事务音讯标记为可投递,订阅方最终将收到该音讯;服务端收到 Rollback 状态则删除半事务音讯,订阅方将不会承受该音讯
事务音讯回查步骤如下:
- 在断网或者是利用重启的非凡状况下,上述发送步骤的步骤 4 提交的二次确认最终未达到服务端,通过固定工夫后服务端将对该音讯发动音讯回查
- 发送方收到音讯回查后,须要查看对应音讯的本地事务执行的最终后果
- 发送方依据查看失去的本地事务的最终状态再次提交二次确认,服务端仍依照发送步骤的步骤 4 对半事务音讯进行操作
6.2、最大致力告诉
最大致力告诉型的指标是 事务发起方尽量将业务处理结果告诉到参与方。实用于一些最终一致性工夫敏感度低,且参与方的处理结果不影响发起方的处理结果的这类告诉类的业务场景。如短信供应商的回执告诉:
最大致力告诉型的实现计划,个别合乎以下两个特点:
- 音讯反复告诉
- 在业务流动发起方实现业务解决之后,向参与方发送音讯,参与方可能没有接管到告诉,此时要发起方有肯定的机制对音讯反复告诉(通常是发起方调用参与方的 http 接口,而且会协商一个 N 次 告诉的下限)
- 定期校对
- 事务发起方提供音讯校对的接口,如果事务参与方没有接管到发起方发送的音讯,能够调用事务发起方提供的接口被动获取音讯
6.3 最大致力告诉 VS 可靠消息
1)可靠性的保障方不同
- 可靠消息计划中,发起方须要保障将音讯收回去,并且将音讯发到参与方,音讯的可靠性要害由发起方来保障
- 最大致力告诉计划中,发起方尽最大致力将业务处理结果告诉给参与方,但参与者是可能接管不到音讯,此时须要接管告诉方被动调用发动告诉方的接口查问业务处理结果,音讯告诉的可靠性要害在参与方
2)两者的业务利用场景不同
- 可靠消息计划 关注的是整体业务解决的事务统一,以异步的形式实现整个业务解决,通常是外部零碎之间的调用
- 最大致力告诉 关注的是业务解决后的告诉事务,行将业务处理结果牢靠的告诉进来
3)技术解决方向不同
- 可靠消息计划 要解决音讯从收回到接管的一致性,即音讯收回并且被接管到
- 最大致力告诉计划 无奈保障音讯从收回到接管的一致性,只提供音讯接管的可靠性机制。牢靠机制是,尽最大致力将音讯告诉给参与方,当音讯无奈被参与方接管时,由参与方被动查问音讯(业务处理结果)
七、弥补型事务模式
AT、TCC、Saga 都是 弥补型的事务模式,利用层面的来提供分布式事务能力,不必依赖数据库对协定的反对,齐全剥离了分布式事务计划对数据库在协定反对上的要求(注意下图中 RM 已从 DB 层移至应用层)。
AT、TCC、Saga 也是两阶段解决。有材料中形容 AT、TCC、Saga 属于 2PC 变种,也有一些文章中用 2PC 来代指 XA,在浏览材料时需依据上下文来辨识。