共计 6387 个字符,预计需要花费 16 分钟才能阅读完成。
浅谈分布式事务那些事
本篇文章咱们重点探讨对于分布式事务的一些相干知识点。这是学习分布式系统的一个必不可少的技术。咱们最常见的案例就是银行转账问题,A 账户向 B 账户转 100 元,那么 A 账户余额要缩小 100,B 账户上要减少 100。两个步骤必须都要胜利才算胜利,只胜利一个的话该当回滚掉。如果 A 和 B 不在同一个环境或者零碎上,这个事务就是分布式事务了,那么在这种状况下,如何保障事务的正确执行,有哪些执行计划呢?
CAP 实践和 BASE 实践
在将分布式事务的开始,咱们先从几个分布式系统方面的根底知识点说起,首先是赫赫有名的 CAP 实践,CAP 三个字母代表了三个单词,Consistency(一致性),Availability(可用性),Partition Tolerance(分区容忍性)。CAP 实践指的是任何一个分布式系统,最多只能保障 CAP 三项中的两项。
首先,一致性,它示意所有的数据节点上的数据要保障是统一且正确的。可用性指的是每一个操作总是可能在肯定工夫内返回后果即是说每个读写操作都是胜利的。咱们平时常常看到的一些对于零碎稳定性的形容都是达到了几个 9,比方 3 个 9 就是 99.9%,4 个 9 就是 99.99%。这就意味着零碎只有在极少数状况下才会产生故障或谬误。分区容忍性指的是当呈现局部节点故障时,分布式系统依然可能失常运行。
常见的分布式系统利用架构都是 AP 或者 CP,因为如果没有 P 那实质上就是个单机利用谈不上分布式了。工作中最罕用到的几个注册核心,ZooKeeper 就是基于 CP 架构的,Eureka 是基于 AP 的。
在工程实际中,基于 CAP 实践又演化出一个新的实践 -BASE 实践。Base 代表了三个单词:Basically Available(根本可用),Soft State(软状态)和 Eventally Consistent(最终一致性)。它的核心思想是 最终一致性,即无奈做到强一致性,但咱们能够依据理论业务状况,使零碎达到最终一致性。
首先,Base 实践中的根本可用,就是不谋求 CAP 中的任何时候的读写操作都是胜利的,但零碎可能保障根本运行。比方咱们平时双十一的时候可能会呈现排队或者失败的状况,其实就是就义了局部可用性来保证系统的稳固。
软状态能够对标 ACID 中的强一致性,ACID 中要么全做要么全不做,所有用户看到的数据都是相对统一的。但软状态是容许呈现数据不统一的时刻,相当于是一个中间状态,几个数据节点的数据呈现了提早的状况。
最终一致性指的是数据不能始终处于软状态的状况,最终还是要达到所有节点数据统一的。这也是咱们大部分开发过程中所谋求的。
数据一致性模型
数据一致性模型个别分为弱一致性和强一致性,上述的 BASE 实践是实现的最终一致性其实就是弱一致性,而强一致性有时候也称为线性一致性,就是每次更新操作后,其余每个过程获取到的数据都是最新的,这种形式对用户敌对,即用户之前做了什么操作,下一步就能保障失去什么。但这种形式会就义零碎的可用性,能够了解为咱们实现了 CP 就义了 A。
除了强一致性之外的一致性模型都是弱一致性,也就是说零碎不承诺更新操作后肯定能保障下次能读取到最新数据,要能正确读取到这个数据须要期待一段时间,这个时间差称作“不统一窗口”。
最终一致性是弱一致性的特例,它强调的是所有节点的数据正本,通过一段时间后,肯定能达到全副统一。它的不统一窗口的工夫次要受通信提早,零碎负载和复制正本的个数影响。其依据不同的保障也能够分为不同模型,包含 因果一致性 和会话一致性 等。
因果一致性要求有因果关系的操作程序失去保障,非因果关系的操作程序则无所谓。
会话一致性将对系统数据的拜访过程框定在了一个会话当中,约定了零碎能保障在同一个无效的会话中实现“读己之所写”的一致性,就是在你的一次拜访中,执行更新操作之后,客户端可能在同一个会话中始终读取到该数据项的最新值。
那么为了实现数据一致性,咱们就天然要回到咱们一开始就要说的分布式事务的概念了,它不同于咱们平时单机利用上的服务,因为在数据分布在多台服务器上,咱们就不能用传统的形式来保障事务的正确提交,这就引出了集中针对分布式事务的解决方案。
2PC 两阶段提交
两阶段提交(2PC,Two-phase Commit Protocol)是十分经典的强一致性、中心化的原子提交协定。这个算法蕴含了两类角色,协调者(Coordinator)和参与者(Participants)。所谓的两阶段指的是 筹备阶段 (Commit-request)和 执行阶段(Commit)。
在筹备阶段的时候,协调者告诉各个参与者筹备提交事务,询问它们是否承受。参与者们会各自反馈本人的响应,批准或者勾销(故障),在这个阶段外面,所有参与者都没有进行 commit 事务操作。
协调者收到参与者们的反馈后果后,进行决策,如果所有的参与者都表示同意,则要提交事务,否则就是勾销。此时告诉所有参与者们进行事务提交 / 勾销,参与者们承受到协调者的告诉后进行事务操作 commit/rollback。
然而两阶段提交存在着一些问题,列举如下:
- 资源被同步阻塞:在执行过程中,所有参与者都是事务独占的,也就意味着,当参与者占用公共资源的时候,其余节点拜访公共资源会处于阻塞状态。
- 协调者可能呈现单点故障:协调者作为这个算法中的外围角色,一旦产生故障,参与者会始终阻塞上来。如果在第二阶段产生的,所有的参与者还处于锁定事务资源的状态,但收不到协调者的决策告诉,导致始终锁定无奈持续实现事务。
- Commit 阶段呈现数据不统一:在第二阶段中,协调者发动了 commit 的告诉,但因为网络等起因导致局部节点收到局部节点每收到告诉,会导致没收到的还处于阻塞状态,收到的会进行 commit 操作,从而呈现了数据不统一问题。
XA 标准
因为讲了二阶段提交,那么这里也顺便提一下咱们被问到 MySQL 时常常会说的 XA 标准,因为它实际上是实现了二阶段提交的。它是由 X/Open 组织提出的分布式事务标准,XA 标准次要定义了事务协调者(Transaction Manager)和资源管理器(Resource Manager)之间的接口。
事务管理器负责着协调者的角色,它来负责协调和治理事务,提供给 AP 应用程序编程接口并治理资源管理器。事务管理器向事务指定标识,监督它们的过程,并负责处理事务的实现和失败。资源管理器,能够了解为一个 DBMS 零碎,或者音讯服务器管理系统。应用程序通过资源管理器对资源进行管制,资源必须实现 XA 定义的接口。资源管理器提供了存储共享资源的反对。
目前,支流数据库都提供了对 XA 的反对,在 JMS 标准中,即 Java 音讯服务(Java Message Service)中,也基于 XA 定义了对事务的反对。
依据 2PC 的标准,XA 也是将事务分为两个步骤筹备(Prepare)和提交(Commit)阶段。筹备阶段就是 TM 向 RM 发送 Prepare 命令,筹备提交,RM 执行数据操作后,返回 TM 后果。TM 依据收到的后果,进入提交阶段,告诉 RM 进行 Commit 或者 Rollback 操作。
在 MySQL 中,有两种 XA 事务,一种是外部 XA,一种是内部 XA。
在 MySQL 的 InnoDB 存储引擎中,开启 binlog 的状况下,MySQL 会同时保护 binlog 日志与 InnoDB 的 redo log,为了保障这两个日志的一致性,MySQL 应用了 XA 事务,因为是在 MySQL 单机上工作,所以被称为 外部 XA。外部 XA 事务由 binlog 作为协调者,在事务提交时,则须要将提交信息写入二进制日志,也就是说,binlog 的参与者是 MySQL 自身。
内部 XA 就是典型的分布式事务,MySQL 反对 XA START/END/PREPARE/Commit 这些 SQL 语句,通过应用这些命令,能够实现分布式事务。
3PC 三阶段提交
三阶段提交(3PC,Three-phase commit)是基于 2PC 的改良版本,它引入了两个改变点。
- 引入超时机制,同时在协调者和参与者都退出超时机制。
- 把 2PC 的第一个阶段拆分成了两步:询问,而后再锁资源,最初真正提交。
它的三个阶段叫做 Can Commit, PreCommit 和 Do Commit。
CanCommit 阶段
和 2PC 的筹备阶段很像,就是协调者向参与者发动 CanCommit 申请,参与者返回 yes 或 no。
PreCommit 阶段
协调者依据参与者的反馈状况来决定是否能够持续事务的 PreCommit 操作。依据响应状况,有以下两种可能。
全副返回 ok
- 如果全副返回 OK 的话,就要进行事务预提交,协调者向参与者发送 PreCommit 申请,参与者承受到后进行事务操作但不提交,如果胜利执行了则返回 ACK 响应。
局部返回 ok
- 如果局部返回 OK 可能指的是有局部参与者返回了 NO 或者是超时后协调者仍然未收到参与者的反馈。那么此时进行中断事务,协调者发送中断请求给参与者,参与者收到后进行 abort 中断。
DoCommit 阶段
此阶段进行真正的提交,跟 2PC 的最终阶段有点类似。如果协调者上一步收到了所有的 ACK 则会告诉参与者进行提交操作,参与者收到后进行提交并反馈协调者 ACK。但如果协调者上一步未收到 ACK 响应,同样也要执行中断事务。如果超过超时工夫参与者都没有收到协调者的告诉,则主动进行 Commit。
很显然 3PC 因为协调者和参与者都有了超时机制(2PC 只有协调者有),能够保障资源不会因为协调者的故障而始终锁定,并且因为多了一个 PreCommit 阶段,使得参与者在提交之前的状态是统一的。但这并不能解决最终可能呈现的一致性问题,因为在下面 DoCommit 阶段也有介绍,如果参与者未收到协调者的告诉,达到了超时工夫后,仍然会进行提交,这就呈现了数据的不一致性。
TCC(Try-Confirm-Cancel)
TCC(Try-Confirm-Cancel)的概念来源于 Pat Helland 发表的一篇名为“Life beyond Distributed Transactions:an Apostate’s Opinion”的论文。它的三个字母也对应了三个阶段:
- Try 阶段:调用 Try 接口,尝试执行业务,实现业务查看,预留业务资源。
- Confirm 阶段:确认执行业务操作,不做业务查看,只应用 Try 阶段预留的业务资源。
- Cancel 阶段:跟 Confirm 互斥,两者只能进入其中一个。在业务执行谬误的时候进行回滚,执行业务勾销,开释资源。
Try 阶段失败能够 Cancel,但 Confirm/Cancel 阶段没有,为了解决此问题,TCC 中增加了事务日志,如果 Confirm 或者 Cancle 阶段出错,是容许进行重试的,所以这两个接口须要反对幂等,如果重试仍然失败那就要靠人工染指了。
TCC 的特点
通过下面的形容,很显然,TCC 绝对于之前的 2PC 等形式,它关注的是业务层而不是数据库或者存储资源层面的事务。它的核心思想是 针对每个业务操作,都要增加相应的确认和弥补操作,同时把相干的解决从数据库拿到了业务层,以此实现了跨数据库的事务。
但正因为它是在业务层进行事务的解决,因而对微服务的侵入性强,业务逻辑的每个分支都要实现 Try,Confirm,Cancel 三个操作,而且 Confirm,Cancel 还要实现幂等。另外 TCC 的事务管理器要记录事务日志,也会损耗肯定的性能。
在业务中引入 TCC 个别是依赖独自的 TCC 事务框架,最常见的就是 Seata 框架了,这个能够本人尝试去应用体验下。
基于音讯弥补的最终一致性
在理论生产工作中,咱们还常常会用到一种计划,基于音讯弥补的形式,它是一种异步事务机制。常见的实现计划有本地音讯表、音讯队列等。
本地音讯表
首先说下本地音讯表,它最后是由 ebay 的工程师提出,核心思想是将分布式事务拆分老本地事务进行解决,通过消息日志的形式来异步执行。所以其实就是利用了各零碎本地的事务实现了分布式事务。在本地要创立一张音讯表,业务执行的时候也要往这个音讯表外面寄存一条数据,这样能够保障寄存音讯和业务数据都是同时胜利或失败的。胜利寄存后再进行后续的业务操作,如果胜利了则将音讯状态更新为胜利。如果失败了,首先会有个定时工作常常去扫描未胜利的工作去执行,如果失败也是能够重试的,所以要保障业务解决接口的幂等。
所以能看出本地音讯表的形式是能保障最终一致性的,但可能呈现局部工夫的数据不统一。
可靠消息队列
咱们常见的音讯队列中 RocketMQ 就反对音讯事务。所以我这里讲下 RocketMQ 实现分布式事务。这部分我从阿里云的文档上找的,感兴趣的能够本人去看,显示几个概念:
- 半事务音讯:暂不能投递的音讯,发送方曾经胜利地将音讯发送到了音讯队列 RocketMQ 版服务端,然而服务端未收到生产者对该音讯的二次确认,此时该音讯被标记成“暂不能投递”状态,处于该种状态下的音讯即半事务音讯。
- 音讯回查:因为网络闪断、生产者利用重启等起因,导致某条事务音讯的二次确认失落,音讯队列 RocketMQ 版服务端通过扫描发现某条音讯长期处于“半事务音讯”时,须要被动向音讯生产者询问该音讯的最终状态(Commit 或是 Rollback),该询问过程即音讯回查。
交互流程如下
事务音讯发送步骤如下:
- 发送方将半事务音讯发送至音讯队列 RocketMQ 版服务端。
- 音讯队列 RocketMQ 版服务端将音讯长久化胜利之后,向发送方返回 Ack 确认音讯曾经发送胜利,此时音讯为半事务音讯。
- 发送方开始执行本地事务逻辑。
- 发送方依据本地事务执行后果向服务端提交二次确认(Commit 或是 Rollback),服务端收到 Commit 状态则将半事务音讯标记为可投递,订阅方最终将收到该音讯;服务端收到 Rollback 状态则删除半事务音讯,订阅方将不会承受该音讯。
事务音讯回查步骤如下:
- 在断网或者是利用重启的非凡状况下,上述步骤 4 提交的二次确认最终未达到服务端,通过固定工夫后服务端将对该音讯发动音讯回查。
- 发送方收到音讯回查后,须要查看对应音讯的本地事务执行的最终后果。
- 发送方依据查看失去的本地事务的最终状态再次提交二次确认,服务端仍依照步骤 4 对半事务音讯进行操作。
尽最大致力告诉
和下面的可靠消息队列形式相似的,还有一种计划叫做尽最大致力告诉。它的核心思想是发动告诉方通过肯定的机制最大致力将业务处理结果告诉到接管方。个别也是通过 MQ 去实现的。
它和基于可靠消息统一的区别如下(摘自某博客):
1、解决方案思维不同
可靠消息一致性,发动告诉方须要保障将音讯收回去,并且将音讯发到接管告诉方,音讯的可靠性要害由发动告诉方来保障。最大致力告诉,发动告诉方尽最大的致力将业务处理结果告诉为接管告诉方,然而可能音讯接管不到,此时须要接管告诉方被动调用发动告诉方的接口查问业务处理结果,告诉的可靠性要害在接管告诉方。
2、两者的业务利用场景不同
可靠消息一致性关注的是交易过程的事务统一,以异步的形式实现交易。最大致力告诉关注的是交易后的告诉事务,行将交易后果牢靠的告诉进来。
3、技术解决方向不同
可靠消息一致性要解决音讯从收回到接管的一致性,即音讯收回并且被接管到。最大致力告诉无奈保障音讯从收回到接管的一致性,只提供音讯接管的可靠性机制。牢靠机制是,最大致力的将音讯告诉给接管方,当音讯无奈被接管方接管时,由接管方被动查问生产(业务处理结果)。
总结
2PC 和 3PC 都是一种强一致性事务,基于数据库层面,但也存在一些数据不统一的危险。TCC 是一种补偿性的事务思维,因为须要在业务层实现,所以对业务侵入大。基于音讯弥补的形式,TCC 还有尽最大致力告诉其实都是一种柔性事务,都是保障了最终一致性,容许呈现局部时刻数据不统一的状况。
这篇文章有点水,其实就是讲了点概念,很多还是摘录自其余博客和拉勾教育 - 分布式技术原理与实战 45 讲这门课程的,就当个读书笔记看吧,至多扩大了一些对分布式事务的根底概念的理解。