zoukankan      html  css  js  c++  java
  • 分布式事务方案-补充

      在分布式时代,分库分表是非常常见的,在微服务系统中,各个系统通常有自己的独立的数据库,所以,事务很难靠数据库本身保证,只能靠业务系统来控制。这就是分布式事务的由来。

     在之前分布式一致性问题博客中提到了关于分布式事务一致性问题的两种解决方案,2PC和3PC,2PC用在数据库层面解决数据库之间的分布式事务;

    因此实际应用中往往不会采用这两个方案,下面说一下其他的解决方案

    TCC:

      TCC是支付宝提出的分布式事务解决方案,是try、confirm、cancel的简写。TCC是应用层面的分布式系统的分布式事务解决方案。

      服务设计:

        每个分布式事务的参与者都需要实现3个接口:try、confirm、cancel。try操作作为第一阶段,负责资源的检查和预留;confirm操作为第二阶段提交操作,执行真正

        的业务;cancel是预留资源的取消。

        TCC分为2个阶段:

        1)准备阶段

          调用方调用各个服务的try接口,各个服务执行资源检查和锁定,看自己是否有能力完成,如果允许,则锁定资源

        2)提交阶段

          如果各个服务的try接口都返回了yes,则进入提交阶段,执行真正的业务,调用方调用各个服务的confirm接口,然后各个服务对try阶段锁定的资源进行处理。

          如果try阶段有一方返回了no,或者超时,调用方调用各个服务的cancel接口,释放锁定的资源。

          对于异常情况,不管是confirm还是cancel失败了,处理方法就是不断重试。

        TCC服务要保证第一阶段try操作成功之后,二阶段Confirm操作一定能成功。

      

      流程示例:

        以转账为例,有2两个服务,扣钱服务、加钱服务,都各自实现try、confirm、cancel接口。

        需求:现在账号A要给账号B转30元

          阶段1:准备阶段

            调用者调用2个服务的try接口。

            1):扣钱服务的try接口对A中的余额进行验证,如果可用余额不少于30元,则冻结30元,返回yes,否则返回no。

            2):加钱服务的try接口对B账号进行校验,如是否合法可用状态,是则返回yes,否则返回no

            这个阶段保证了扣钱可扣,加钱可加

          阶段2:提交阶段

            两个服务的try接口都返回了yes,调用者调用2个服务的confirm接口。扣钱服务实际扣减A的30元,加钱服务实际给B加30元

            try接口如果有返回no或者超时的,调用者调用2个服务的cancel接口,扣钱服务解冻A的30元,加钱服务什么也不用做。

      异常控制:

        下面是典型的异常情况

        1、空回滚

        现象是try没执行就调用了cancel,例如在某些异常情况下,调用try时出现异常,try接口实际没有被调用,那么就没有返回yes;按照正常流程进入第二

        阶段,调用cancel接口,这就造成了空回滚。

        解决方案:让cancel能够识别出这是一个空回滚,可以记录事务执行状态,cancel中判断try是否执行了

        2、幂等

        无论是网络数据包重传,还是异常事务的补偿执行,提交阶段可能会重复调用confirm和cancel,所以要实现幂等,保证多次执行效果一致。

        解决方案:记录事务执行状态,如果执行过了,就不再执行

        3、悬挂

        现象是先执行了cancel,后执行了try,造成资源没人释放。例如调用try时网络拥堵超时,被认为失败啊,然后调用cancel,这时事务相当于结束了,但后来

        网络好之后try开始执行了,锁定了相关资源,因为事务已经结束,confirm和cancel都不会再调用,造成资源悬挂,无法释放

        解决方案:还是记录事务执行状态,try执行时判断cancel是否执行了

        4、业务数据可见性控制

        TCC服务的一阶段try操作会做资源的预留,在二阶段操作执行之前,如果其他事务需要读取被预留的资源数据,那么处于中间状态的业务数据该如何向用户展示,需要业务在实现时考虑清楚;通常的设计原则是:宁可不展示、少展示,也不多展示、错展示。

        5、业务数据并发访问控制

        TCC服务的一阶段Try操作预留资源之后,在二阶段操作执行之前,预留的资源都不会被释放,如果此时其他分布式事务修改这些业务资源,会出现分布式的并发问题。因此用户在实现TCC服务时,需要考虑业务数据的并发控制,尽量将逻辑锁粒度降到最低,以最大限度的提高分布式事务的并发性;

      总结

      优点:可靠性高,实用性高

      缺点:开发复杂度高,需要多次DB操作,性能有一定损耗。

    内容参考性能与架构公众号

    最终一致性:

      最终一致性通常都是使用消息中间件来实现的,系统结构如下:

      

      用户向系统A发起转账请求,A先在自己的数据库中扣钱,然后通过消息中间件告诉B应该加钱,B收到消息后在自己的数据库中加钱。

      但是这里有个问题,A更新数据库和给消息中间件发送消息是2个操作,可能会出现如下场景:

        1):先更新数据库,成功了,但发送消息失败了,重发多次还是失败

        2):先发消息成功了,但数据库更新失败,消息撤不回来了

      因为这两个操作不是原子性的,所以先发谁都可能存在问题。可以将更新数据库和给消息中间件发送消息放到一个事务中吗?

      这样就保证了原子性,但是也存在下面两个问题:

        1):如果消息发送失败,有两种可能

          (1):消息中间件根本没有收到消息的异常,这时数据库回滚没有问题,A没有扣钱,B也没有加钱

          (2):消息中间件收到消息后response时出错了,这时数据库回滚A没有扣钱,但实际上B收到了消息,加钱了,

            这就出现了不一致的问题

        2):如果发消息时网络延迟很高,数据库事务一直被拖着,导致性能很差。

      因此,将更新数据库和发消息到消息中间件也是不可取的。

    解决方法:为了保证原子性,增加一个消息表,使A扣钱和写消息放在一个事务中,A不直接往消息中间件发送消息,而是把消息写入消息表

      通过一个后台程序不断的把消息写入消息中间件。

      

      这个后台程序源源不断的把消息表中的消息发到消息中间件,如果失败就重试,就可以保证:

      1、消息不会丢失

      2、顺序不乱

      但是可能会有消息重复的情况,因为消息发送失败可能是写入失败,也可能写入成功但响应失败,后台都会重试,所以消息可能会重复,这个问题需要B系统来处理。

      系统B需要考虑2个问题:

      1、消息丢失

      B从消息中间件中拿到消息,还没处理完就宕机了,这条消息怎么办?这时,需要通过ACK机制来处理,消费成功的发送ACK,对于没有ACK的消息,消息中间件会再次推送。

      2、消息重复

      ACK机制也存在消息重复的情况,比如B已经 处理完一条消息,发ACK时失败了,那么这条消息还会被推送过来,还有后台程序也可能发送重复的消息,对于消息重复的问题,可以再加一张判重表,记录处理成功的消息,每次收到消息,先通过判重表判断一下,如果重复了就不处理,实现幂等性。

      这样,最终的结构为:

      

      这就是最终一致性解决分布式事务问题的基本思路,A保证消息不丢,B保证消息不漏和幂等。

      

  • 相关阅读:
    数据库 连接(join)
    Linux top
    Game2048
    黑豆白豆问题
    1000个苹果10箱
    Jconsole
    八数码 Java实现
    两数之和
    磁盘调度算法
    常见应用网络层次
  • 原文地址:https://www.cnblogs.com/yangyongjie/p/11008038.html
Copyright © 2011-2022 走看看