分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。一个大的操作由 N
多的小的操作共同完成。而这些小的操作又分布在不同的服务上。针对于这些操作,要么全部成功执行,要么全部不执行。
举个例子:
转账是最经典的分布式事务场景,假设用户 A 发起一笔跨行转账给用户 B,银行系统首先扣掉用户 A 的钱,然后增加用户 B
账户中的余额。如果其中某个步骤失败,此时就有可能会出现两种异常情况:
实际上,两种情况都是不允许发生的,此时就需要事务来保证转账操作的成功。
在单体应用中,只需要@Transactional就可以开启事务来保证整个操作的原子性。
但实际的应用架构中,不可能是单体的服务,如分布式微服务架构:
比如下单服务,扣库存服务等等,必须要保证不同服务状态结果的一致性
,于是就出现了分布式事务。
在一个分布式系统中,以下三点特性无法同时满足:
一致性(C):undefined 在分布式系统中的所有数据备份, 「在同一时刻是否拥有同样的值」 。(等同于所有节点访问同一份最新的数据副本)
可用性(A):undefined 在集群中一部分节点 「故障」 后,集群整体 「是否还能响应」 客户端的读写请求。(对数据更新具备高可用性)
分区容错性(P):undefined 即使出现 「单个组件无法可用,操作依然可以完成」 。
具体地讲在分布式系统中,在任何数据库设计中,一个Web应用 「至多只能同时支持上面的两个属性」
。显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择。
分布式系统,往往追求的是可用性,它的重要程序比一致性要高,那么如何实现高可用性呢?
那就是 BASE 理论,它是用来对 CAP 定理进行进一步扩充的。BASE 理论指的是:
BASE 理论是对 CAP
中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:`无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual
consistency)。`以下就是分布式事务解决方案。
熟悉 MySQL 的同学对两阶段提交
应该颇为熟悉,[MySQL
的事务就是通过日志系统来完成两阶段提交的](https://www.jianshu.com/p/f3a1e17a4df6)。
两阶段协议可以用于单机集中式系统,由事务管理器协调多个资源管理器;也可以用于分布式系统,由一个全局的事务管理器协调各个子系统的局部事务管理器完成两阶段提交。该协议有两个角色:A
节点是事务的协调者,B/C 是事务的参与者。
当 A 节点收到 B/C 参与者所有的确认消息后;
相对于 2PC,增加了 CanCommit 阶段和超时机制。如果段时间内没有收到协调者的 commit 请求,那么就会自动进行 commit,解决了 2PC
单点故障的问题。但是性能问题和不一致问题仍然没有根本解决。
这个阶段所做的事很简单,就是协调者询问事务参与者,是否有能力完成此次事务。如果都返回 yes,则进入第二阶段;有一个返回 no
或等待响应超时,则中断事务,并向所有参与者发送 abort 请求
此时协调者会向所有的参与者发送 PreCommit 请求,参与者收到后开始执行事务操作,并将 Undo 和 Redo
信息记录到事务日志中。参与者执行完事务操作后(此时属于未提交事务的状态),就会向协调者反馈“Ack”表示已经准备好提交了,并等待协调者的下一步指令。
在阶段二中如果所有的参与者节点都可以进行 PreCommit 提交,那么协调者就会从“预提交状态”转变为“提交状态”。然后向所有的参与者节点发送
doCommit 请求,参与者节点在收到提交请求后就会各自执行事务提交操作,并向协调者节点反馈 Ack 消息,协调者收到所有参与者的 Ack
消息后完成事务。相反,如果有一个参与者节点未完成 PreCommit 的反馈或者反馈超时,那么协调者都会向所有的参与者节点发送 abort
请求,从而中断事务。
TCC
其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。
它分为三个阶段:Try、Confirm、Cancel。
比如下订单减库存:
执行流程:
总之,TCC 就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,并且很大程度的增加了业务代码的复杂度。因此,这种模式并不能很好地被复用。
执行流程:
消息事务的原理是将两个事务通过消息中间件进行异步解耦,和上述的本地消息表有点类似,但是是通过消息中间件的机制去做的,其本质就是“将本地消息表封装到了消息中间件中”。
执行流程:
这种方案也是实现了 「最终一致性」 ,对比本地消息表实现方案,不需要再建消息表, 「不再依赖本地数据库事务」
了,所以这种方案更适用于高并发的场景。目前市面上实现该方案的 「只有阿里的 RocketMQ」 。
最大努力通知的方案实现比较简单,适用于一些最终一致性要求较低的业务。
执行流程:
长时间运行的事务
其核心思想是将长事务拆分为多个本地短事务,由 Saga
事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。Seata 框架中一个分布式事务包含三种角色:
「Transaction Coordinator (TC)」 :事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
「Transaction Manager (TM)」 :控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
「Resource Manager (RM)」 :控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
seata框架 「为每一个RM维护了一张UNDO_LOG表」 ,其中保存了每一次本地事务的回滚数据。
具体流程:
* 如果相同,根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句并执行,然后提交本地事务达到回滚的目的,最后释放相关记录的全局锁。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。