分布式事务--各方案的最通俗讲解

分布式事务的方案:
XA/TCC/本地消息表/最大努力通知/可靠消息最终一致性/(saga/seata)
 
1.XA(二段提交/2PC) :数据库有支持XA的协议的,所以其实XA是在数据库层面的操作,不是服务层面的操作
第一阶段:询问
第二阶段:执行
一般是在多数据源跨库上执行~不适合在微服务架构上,微服务架构思想是每个微服务只执行一个库,需要调用业务只能通过暴露接口进行
 
---------------------------------- 
2.TCC(try-confirm-cancel)
try:主要是做检查后续的操作能否执行,例如扣款的话需要锁住金额同时查看扣款余额是否足够等
confirm:执行具体的操作
cancel:是一个补偿机制需要写补偿逻辑,例如某一个服务扣款失败,某一个成功,那么成功的需要把原来扣取的款给加回去
特点:是跟业务严重的耦合和侵入,同样的cancel不同业务实现都会不一样,一般在严重需要一致性的场景才会使用TCC 例如跟资金相关的~
 
ps:TCC需要事务协调器来协助完成上面操作这些东西就不说了,都是实现细节,具体可以看对应的文章
 
----------------------------------
3.本地消息表方案(ebay采用)
   1)主动方和被动方都要在本地存一个消息表
   2)主动方执行成功后在他的本地消息表上同时插入一条消息状态为'待确认'(注意:主动执行方操作和本地消息表的操作需要在同一事务上),同时发送一条消息到MQ让被动方消费,同时想办法监听被动方是否执行成功(轮训或者基于zookeeper监听)
   3)被动方消费数据,先在本地消息表插入数据,同时执行自己的业务(注意:被动方业务操作和本地消息表在同一事务上,同时本地消息表需要有唯一键保证不重复消费)
   4)如果成功主动方更新自己的本地消息表的状态为'成功',否则定时轮训'待确认'的消息表知道发送到MQ上让被动方执行
缺点:大量依赖本地消息表
 
---------------------------------- 
4.最大努力通知方案
   业务活动的主动方在完成处理之后向业务活动的被动方发送消息,允许消息丢失。业务活动的被动方根据定时策略,向业务活动的主动方查询,恢复丢失的业务消息
 
---------------------------------- 
5.可靠消息最终一致性方案(这个方案使用比较多,有点类似本地消息表只不过消息表放在MQ,一般用于扣库存)
    1)就是基于MQ的事务机制(RocketMQ[RocketMQ如果发现主动方成功了,发送确认给MQ时失败,RocketMQ会主动轮训自己存储的prepare的消息,超过一定时间的数据会回调对应主动方的回调接口询问情况]或者RabbitMQ),保证消息发送和主动方事务在同一事务中,保证主动方成功消息发送成功主动方失败消息失败
    2)接着就是被动方消费消息,直至到成功为止(注意消费需要保证幂等),如果消费放多次都不成功,那么就想办法通知主动方进行回滚或者人工来回滚和补偿
 
----------------------------------
saga模式和TCC区别
  简单点saga就是比TCC少了try:
   Saga的核心就是补偿,一阶段就是服务的正常顺序调用(数据库事务正常提交),如果都执行成功,则第二阶段则什么都不做;但如果其中有执行发生异常,则依次调用其补偿服务来保证最终的一致性。
   TCC的特点在于业务资源检查与加锁,一阶段进行校验,资源锁定,如果第一阶段都成功,二阶段对锁定资源进行交易逻辑,否则,对锁定资源进行释放。
 
---------------------------------
seata原理(AT模式)(跟XA不一样的是第一阶段就提交,第二阶段是进行是否回滚的确认):
实际就是第一阶段先提交并保留需要回滚的信息,然后第二段就确认是否要回滚,他主要特点是可以解析SQL提交前的数据信息后续用于回滚
流程如下:
第一阶段:
    1.解析sql语句,得到 SQL 的类型(UPDATE),表(product),条件(where name = 'ABC')等相关的信息。
    2.查询老数据,根据上面的where语句sql,去数据库查询原始的数据。
            如 select * from product where name = 'ABC';得到原始的数据,如该行id=1,然后记录下来。
    3.执行第一步的sql语句,即执行update,修改数据库的该记录的值。(默认是隔离级别是读未提交)
    4.查询修改后的值,select * from product where id =1.得到该行值,记录下来。
    5.插入回滚日志,将老值、新值以及sql语句组成一个将来可用于回滚的日志,插入到UNDO_LOG表
    6.向TC server注册分支,申请product表,id=1的行的全局锁。注意,这个全局锁是相对于所有可能的同时在执行的分布式事务而言的。一旦某个分支,获取了该记录的全局锁,在解锁之前,任何其他的分布式事务,不能修改该数据。
    7.本地事务提交,将自己的本地事务、和前面的UNDO LOG一起提交。
    8.将本地事务提交的结果上报给TC server。如成功、失败
第二阶段:
    成功的情况:分支收到了TC下发的成功请求,立马返回我已OK的结果给TC,然后异步执行删除UNDO LOG的操作。因为成功了,所以用来回滚的UNDO LOG就没意义了,异步删除掉就好。
    失败的情况:
        1.分支收到了TC下发的失败请求,开始执行回滚逻辑。
        2.通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
        3.数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。
        4.根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句。
          update product set name = 'ABC' where id = 1;
        5.提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC 
 
---------------------------------
ps:
1.分布式事务不应该太多~应该尽量通过监控错误日志信息的方式 通知快速修复数据等,大量的分布式事务会增加系统的复杂性降低吞吐量~90%的都不应该用分布式事务解决
2.一般扣库存,订单那些采用可靠消息最终一致性性方案,跟金钱有关需要严格保证的采用TCC,但是像发短信,积分等等不太重要的业务不要使用分布式事务解决,通过事前监控时候快速通知解决来搞定
 
 

猜你喜欢

转载自www.cnblogs.com/danvid/p/12202727.html
今日推荐