分布式事务与Seate框架(1)——分布式事务理论

前言

  虽然在实际工作中,由于公司与项目规模限制,实际上所谓的微服务分布式事务都不会涉及,更别提单独部署构建Seata集群。但是作为需要不断向前看的我,还是有必要记录下相关的分布式事务理论与Seate框架,甚至Seate框架的源码分析,先从分布式事务理论开始吧,下一部分将介绍对Seata的应用,最后再对核心的源码进行跟踪分析并学习!

  主要参考《Spring Cloud Alibaba 微服务原理与实战》中分布式事务章节,有需要资源的朋友可以评论我!(文中的截图均来自此,由于相对好理解所以没有自己画图)

   介绍完理论就应该进行实践,我的下一篇博文(分布式事务与Seate框架(2)——Seata实践)代码实践,两篇博文配合学习,效果应该不错

 


 

一、分布式事务理论模型

在介绍相应的分布式事务理论模式前,先放出如下图,对分布式事务常见的解决方案作对比:

  

最主要的区别就是对一致性的要求:是强一致性还是最终一致性,根据这个核心展开来介绍相关的几种理论介绍

1.X/Open分布式事务模型

  X/Open DTP是由X/Open组织提出的一套分布式事务的标准,这个标准提出使用了2PC(Two-Phase-Commit)来保证分布式事务的完整性

X/Open DTP包含三种角色:

  • AP: Application 表示应用程序
  • RM: Resource Manager 资源管理器,比如数据库
  • TM: Transaction Manager 表示事务管理器,协调事务和管理资源,类似于Spring的Transaction Manager。

      

在分布式环境下,RM代表数据库,可能有多个,所以TM需要管理多个数据库的事务,也就是说TM就是一个全局事务管理器。步骤如下:

  • 配置TM,多个RM注册到TM上,相当于TM注册RM作为数据源;
  • APTM管理中的RM获取连接,如果RM是数据库则获取JDBC连接;
  • APTM发起一个全局事务,生成全局事务IDXID),XID通知各个RM
  • AP获取到连接后直接操作RM,此时AP每次操作时会把XID传递给RM
  • AP结束全局事务,TM会通知各个RM全局事务结束;
  • 根据各个RM的事务执行结果,执行提交或者回滚操作

需要注意的是:TM与多个RM之间的事务控制,是基于XA协议来完成的,XA协议是X/Open提出分布式事务处理规范。  

       

2.两阶段提交协议

根据上图可知,2PC具体步骤:

  • 准备阶段TM通知RM准备分支事务,记录事务日志,并告诉事务管理器的准备结果。
  • 提交/回滚阶段如果所有的资源管理器RM在准备阶段明确返回成功,则TM向所有的资源管理器PM发起事务提交完成数据的变更,反之如果有任何一个资源管理器RM明确返回失败,则TM会向所以RM发  送事务回滚指令。

  

但也有如下的缺点:

  • 同步阻塞:所有的RM都需要及时反馈,否则会一直阻塞,占用资源不释放。
  • 事务协调者TM的单点故障:如果TM在第二阶段出现故障,那么其它参与者RM会一直处于锁定状态;
  • “脑裂”导致数据不一致问题:在第二阶段commit提交时,只有部分RM接受到了并且完成commit提交了事务,但是由于网络问题,导致只有一部分RM未收到commit导致无法提交事务,会出现数据不一致的问题

3.三阶段提交协议

 三阶段提交协议是两阶段的改良版,利用了超时机制解决了同步阻塞的问题,步骤如下:

  第一阶段:CanCommit(询问阶段)事务协调者向参与者发送事务执行请求,询问是否可以完成指令,参与者只需要回答是或者不是,会有超时机制;

  第二阶段:PreComiit(准备阶段)事务协调者会根据参与者的反馈结果决定是否继续执行,如果在第一个阶段CanCommit都收到了请求的话,就开始执行写redo和undo日志,并且返回ACK给协调者,这里即是二阶段提交的第一阶段

  第三阶段:DoCommit(提交或回滚阶段)如果在第二阶段PreCommit提交成功以后,那么事务协调者会向所有的参与者发起事务提交指令,如果其中某个参与者返回失败,则执行终止指令回滚事务。

      

相比较二阶段提交协议,三阶段提交协议有以下不同:

1)增加了CanCommit阶段:可以尽早发现参与者无法执行的情况,及时中断;

2)增加了超时机制:参与者与事务协调者都引入了超时机制,一旦超时,事务协调者和参与者会继续提交事务,并且任务处于成功状态(因为在这种情况下事务默认为成功的可能性比较大),事实上,第三阶段提交协议下仍然可能出现不一致的情况(但是概率很小)

4.CAP理论和BASE理论

(1)CAP定理

CAP定理,又称布鲁尔定理,简单来说就是指在分布式系统中不可能同时满足一致性(C:Consistency)、可用性(A:Availability)、分区容错性(P: Partition Tolerance)

  C:数据在多个副本中保持一致,比如前面说的分布式数据一致性问题

  A:系统对外提供的服务必须一直处于可用状态;

  P:在分布式中遇到任何网络分区故障,系统仍然能够正常对外提供服务。

CAP定理证明,在分布式系统中,要么满足CP、要么满足AP。不可能实现CAP或者CA,原因是网络通信并不是绝对可靠的。而在分布式系统中即便出现网络故障也需要保证系统仍然能够正常对外提供服务,Partition Tolerance是必然存在的,所以P是一定会有的,只有C与A不能同时兼得

  AP:相当于放弃了强一致性,实现最终的一致性,这是很多解决分布式数据一致性的最终选择

  CP:放弃了高可用性,实现强一致性,之前的2PC与3PC都采用这种方案,可能会导致用户完成某个操作会等待较长时间。

(2)BASE理论

BASE理论是由于CAP中一致性和可用性不可兼得而衍生出来的一种新思想,BASE理论的核心思想通过牺牲数据的强一致性获得高可用性

  Basically Available(基本可用):分布式系统出现故障时,允许损失一部分功能的可用性,保证核心功能的可用。

  Soft State(软状态):允许系统中的数据存在中间状态,这个状态不影响系统的可用性,也就是系统中不同节点的数据副本之间的同步存在延时

  Eventually Consistent(最终一致性):中间状态的数据经过一段时间之后,会达到最终的数据一致性。

  也就是BASE理论并没有要求数据的强一致性,而是允许在一段时间是不一致的,但最终数据会在某个时间点实现一致。比如:用户发起订单支付,不需要同步等待支付的执行结果,系统会返回一个支付处理中的状态到用户界面,最后可以通过订单详情查看到支付处理结果,而对于系统来说,当第三方支付处理成功之后,再更新订单的状态即可。

二、分布式事务问题常见的解决方案

1.TCC补偿型方案(互联网最常用的方案)

TCC 是一种比较成熟的分布式数据一致性解决方案,实际上是把一个完整业务拆分为三个步骤:

  Try:主要是数据的校验或者资源的预留;

  Confirm:确认真正执行的任务,只操作Try阶段预留的资源;

  Cancel:取消执行,释放Try阶段预留的资源。

其实TCC是2PC的思想,第一阶段通过Try进行准备工作,第二阶段Confirm/Cancel表示Try阶段操作的确认和回滚

  比如:用户通过账户余额购买一个理财产品,涉及两个事件,对应两个不同的微服务中的方法:账户服务中,对用户账户余额进行扣款;理财产品中,对指定产品可申购金额进行扣减。则需要TCC补偿方案控制:

  • 在账户服务中Try方法对余额进行冻结Confirm方法把Try方法冻结的余额进行实际扣款Cancel方法把Try方法冻结余额进行解冻
  • 在账户服务中Try方法对本次申购部分额度进行冻结Confirm方法把Try方法冻结的额度进行实际减扣Cancel方法把Try方法冻结额度进行释放
  • 而主业务方法中就会调用两个微服务业务中的方法(即对余额减扣、申购额度减扣),就会先调用Try对资源预留,如果Try阶段都正常,则进行Confirm对预留资源进行实际应用,如果不正常则Cancel取消对资源的预留,对资源进行回滚,从而保证数据的一致性。

   而需要注意的是,微服务框架宕机或者网络异常导致没法完成Cancel/Confirm请求,TCC事务框架会记录一些分布式事务的操作日志,保存分布式事务运行的各个阶段和状态,之后TCC事务协调器根据日志进行重试,达到数据的最终一致性

2.基于MQ最终一致性方案(基于具有事务模型消息的MQ,如RocketMQ)

  基于可靠消息的一致性是互联网公司比较常用的分布式数据一致性解决方案,比如支付服务于账户服务之间的流程需要MQ:

      

但是支付服务本地事务到MQ发送消息存在非原子操作问题,如果先执行本地事务,再发送消息到MQ,MQ可能出现超时情况,导致本地事务可能回滚,从而导致数据不一致

如果先发送消息,再执行数据库事务,在这种情况下可能会出现消息发送成功但是本地事务更新失败的情况下,仍然存在数据不一致的问题

针对MQ与服务之间的数据不一致的情况,我们可以采用MQ的事务消息模型,比如RocketMQ为例:

  • 生产者发送事务消息到消息队列,消息队列此时只记录消息的数据,消费者无法消费此信息;
  • 生产者之后执行本地事务,根据执行结果发送一条确认消息给消息队列服务器,告诉消费者是否消费该消息;如果生产者本地事务执行成功则发送一条Commit消息,即告诉消费者可以消费该消息,否则,消息队列服务器就会删除该消息;
  • 如果在生产者执行本地事务的过程中因为某些情况一直未给消息队列服务器发送确认,那么消息队列服务器就会主动回查生产者执行本地事务的结果,然后根据结果执行上述步骤;
  • 消息队列服务器上存储的消息被生产者确认后,消费者就可以消费该消息,最后发送一个确认标识给消息队列服务器,表示该消息投递成功

 

由上我们可知:

  • 在RocketMQ事务模型中,事务是由生产者完成的,当消息没有签收的情况下,MQ队列服务会重复投递。
  • RocketMQ的事务消息模型最核心的就是事务回查(在没有收到生产的commit/rollback的情况下,主动查询事务状态)。

3.最大努力通知型

  所谓的最大努力通知就是在客户端没有返回消息确认时,支付宝会不断地进行重试,知道收到一个消息确认或者达到最大重试次数。

  可以参考支付宝支付的例子,如果商户不返回SUCCESS标识,每隔1min、5min…会不断通知商户支付结果,达到最大次数以后就不通知,将会同时提交查询结果,定时任务出发查询。

三、Seata框架的分布式事务模式

Seata是致力于微服务架构下提高性能和简易使用的分布式事务,它提供了AT、TCC、Saga和XA事务模式。

1.AT模式

AT模式是Seata最主推的分布式事务且基于XA演进而来的解决方案,主要有三个角色:TM、RM和TC,其中TM和RM作为Seata的客户端和业务集成:

  • TC作为Seata服务器独立部署。
  • TM向TC注册一个全局事务,并生成全局唯一的XID;

在AT模式下,数据库资源被当做RM,访问RM时,Seata会对请求进行拦截;

每个本地事务提交时,RM会向TC(Transaction Coordinator,事务协调器)注册一个

具体步骤如下:

  1) TM向TC注册全局事务,并生成全局唯一XID;

  2) RM向TC注册分支事务,并将其纳入到该XID对应的全局事务范围。

  3) RM向TC汇报资源的准备状态

  4) TC汇总所有事务参与者的执行状态,决定分布式事务全部回滚还是提交

  5) TC通知所有的RM提交/回滚事务

AT模式和XA类似,也是一个2PC模型,但实际上做了很多优化,后面再介绍

2.Saga模式

 Saga模式又称之为长事务解决方案,核心思想是:把一个业务流程中的长事务拆分成多个本地短事务,业务流程中每个参与者事务执行失败,则通过补偿机制前面已经成功的参与者

    

按照Saga的工作模式,一般有两种方式:

  1) T1, T2,T3,T4…,Ti,表示所有事务正常运行

  2) T1, T2,T3,T4…,Tj, Cj,…,C2,C1:表示执行到Tj事务时出现异常,通过补充操作撤销之前的所有成功的sub-transaction

另外,提供两种补偿模式:一是向后补偿,即第二种方式,任一子事务执行失败,则把之前的子事务结果逐一撤销。另一种就是向前恢复,都可以出现失败情况,在最坏的情况下只能进行人工干预处理。

(1)Saga优劣势

优势:

  • 一阶段直接提交本地事务(相比较XA/TCC模式没有Try);
  • 没有锁等待,性能比较高;
  • 在事件驱动模式下短事务可以异步执行;
  • 补偿机制实现比较简单。

劣势:

  不提供原子性与隔离性支持,合理性影响性比较大,比如用户赠送了一张优惠券,但是已经把优惠券用完了,无法对这个sub-transaction进行补偿

(2)Saga实现方式

Saga的整个过程会涉及一个两种协调模式:

1)事件/编排式

  把Saga的决策和执行顺序逻辑分布在Saga的每一个参与者中,它们通过交换事件的方法进行沟通。

  即第一个服务执行完本地事务之后,发送一个事件,这个事件会被一个或多个服务监听,监听到该事件的服务本地事务并发布新的事件,此后一直延续这种事件触发模式,直到该业务流程中最后一个服务的本地事务执行结束,才意味着整个分布式长事务也执行结束。

    

具体步骤如下,可以看出都是由事件发布来驱动事务执行

  • 订单创建新的订单,把订单状态设置为待支付,并发布一个ORDE_CREATE_EVENT事件;
  • 库存服务监听到ORDER_CREATE_EVENT事件后,执行本地的库存冻结方法,如果执行成功,则发布一个ORDER_PREPARED_EVENT事件;
  • 支付服务监听ORDER_PREPARED_EVENT事件后,执行账户扣款方法,并发布PAY_ORDER_EVENT事件;
  • 最后积分服务监听PAY_ORDER_EVENT事件,增加账户积分,并更新订单状态为成功。

上述某个步骤如果执行失败,都会发送一个失败事件,每个服务都会监听失败的情况根据实际需要逐一回滚。

2)命令/协同式

 把Saga的决策和执行顺序逻辑集中在一个Saga控制类中,它以命令/回复的方式与每项服务进行通信,告诉它们应该执行哪些操作

    

命令/协调式的实现步骤如下:

  • 订单服务首先创建一个订单,然后创建一个订单Saga协调器,启动订单事务
  • Saga协调器向库存服务发送冻结库存命令,库存服务通过Order Saga Reply Queue回复执行结果;
  • 接着Saga协调器继续向支付服务发起账户扣款命令,支付服务通过Order Saga Reply Queue回复执行结果。
  • 最后,Saga协调器向积分服务发起增加积分服务,积分服务回复执行结果

值得注意的是,订单Saga协调器必须需要提前知道“创建订单”的所有流程,并且在某个环节执行失败,都需要每个参与者发送命令撤销之前的事务操作

 


  在了解完分布式事务理论部分结束后,接下来记录的就是Seata的实践部分,将会抽时间再写一篇博文,加深印象

 

posted @ 2021-05-08 16:28  JJian  阅读(289)  评论(0编辑  收藏  举报