美文网首页MQ高并发,分布式事务技术文
分布式事务之最终一致的Mq实现

分布式事务之最终一致的Mq实现

作者: jsondream | 来源:发表于2016-07-21 18:32 被阅读5419次

    问题的起源

    分布式系统的特性

    对分布式系统有过研究的读者,可能听说过“CAP定律”、“Base理论”等,非常巧的是,化学理论中ACID是酸、Base恰好是碱。这里我们不对这些概念做过多的解释,有兴趣的读者可以查看相关参考资料。

    这里针对一致性我们做个简单的科普:

    分布式事务有强一致,弱一致,和最终一致性这三种:

    强一致

    当更新操作完成之后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现需要牺牲可用性。

    弱一致

    系统并不保证续进程或者线程的访问都会返回最新的更新过的值。系统在数据写入成功之后,不承诺立即可以读到最新写入的值,也不会具体的承诺多久之后可以读到。

    最终一致

    弱一致性的特定形式。系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。在没有故障发生的前提下,不一致窗口的时间主要受通信延迟,系统负载和复制副本的个数影响。DNS 是一个典型的最终一致性系统

    在分布式系统中,同时满足“CAP定律”中的“一致性”、“可用性”和“分区容错性”三者是几乎不可能的。在互联网领域的绝大多数的场景,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可,这时候我们只需要用短暂的数据不一致就可以达到我们想要效果。

    实例描述

    比如有订单,库存两个数据,一个下单过程简化为,加一个订单,减一个库存。 而订单和库存是独立的服务,那怎么保证数据一致性。

    这时候我们需要思考一下,怎么保证两个远程调用“同时成功”,数据一致?

    请大家先注意一点远程调用最郁闷的地方就是,结果有3种,成功、失败和超时。 超时的话,成功失败都有可能。

    一般的解决方案,大多数的做法是借助mq来做最终一致。

    如何实现最终一致

    实例分析

    我们是怎么利用Mq来达到最终一致的呢?下面让我们来一起进行详细的分析:

    订单业务分析

    首先,拿我们上面提到的订单业务举例:

    • 在我们进行加订单的过程中同时插入logA(这个过程是可以做本地事务的)
    • 然后可以异步读取logA,发mqA
    • B端接收mqA,同时减少库存,B这里需要做幂等(避免因为重复消息造成的业务错乱)

    复杂的混合异步业务调用

    那么我们通过上面的分析可能联想到这样的问题?

    本地先执行事务,执行成功了就发个消息过去,消费端拿到消息执行自己的事务
    比如a,b,c a异步调用b,c, 如果b失败了,或者b成功,或者b超时,那么怎么用mq让他们最终一致呢?b失败就失败了,b成功之后给c发一个消息,b和c对a来讲都是异步的,且他们都是同时进行的话,而且需要a,b,c同时成功的情况,那么这种情况用mq怎么做

    其实做法还是参照于本地事务的概念的。

    • 第一种情况:假设a,b,c三者都正常执行,那整个业务正常结束
    • 第二种情况:假设b超时,那么需要a给b重发消息(记得b服务要做幂等),如果出现重发失败的话,需要看情况,是终端服务,还是继续重发,甚至人为干预(所有的规则制定都需要根据业务规则来定)
    • 第三种情况:假设a,b,c三者之中的一个失败了,失败的服务利用MQ给其他的服务发送消息,其他的服务接收消息,查询本地事务记录日志,如果本地也失败,删除收到的消息(表示消息消费成功),如果本地成功的话,则需要调用补偿接口进行补偿(需要每个服务都提供业务补偿接口)。

    注意事项

    mq这里有个坑,通常只适用于只允许第一个操作失败的场景,也就是第一个成功之后必须保证后面的操作在业务上没障碍,不然后面失败了前面不好回滚,只允许系统异常的失败,不允许业务上的失败,通常业务上失败一次后面基本上也不太可能成功了,要是因为网络或宕机引起的失败可以通过重试解决,如果业务异常,那就只能发消息给a和c让他们做补偿了吧?通常是通过第三方进行补偿,ABC提供补偿接口,设计范式里通常不允许消费下游业务失败

    上面的话我们该怎么理解呢,举个例子吧

    比如A给B转账,A先自己扣钱,然后发了个消息,B这边如果在这之前销户了,那重试多少次也没用,只能人工干预

    阿里在分布式事务采用的解决方式

    阿里部分业务是用Mq实现了最终一致性,也有一部分业务用了tcc事务,但是tcc事务用的比较少,因为会侵染业务,开发成本比较高,如果体量不大的话直接用jta或mq支持事务就好,其实在分布式事务这一块还有一种最大努力型,也比较无脑的一种方式。

    声明

    以上观点均为个人总结,不代表完全正确。

    相关文章

      网友评论

      • Peter潘的博客:东哥牛逼啊
        jsondream:@Peter潘的博客 :scream::scream::scream:这都能被翻到
      • 蕲婼圵渁:关于分布式事务的介绍及解决方案,百度一下真的是一搜一大把,但几乎都是简单的介绍一下基本的概念,和一些简单的场景,比如用户A转账给用户B这种相当简单的场景,但真实的企业级系统,或互联网系统应该远比这个复杂,就拿我曾经参与过的系统来说吧,当然我这里没用MQ,都是同步的RPC调用。

        1、前端页面发起请求到服务A节点(A节点只做服务集成)
        2、服务A 先调用 服务B,服务B会处理自己的业务逻辑,一下就更新了10多个表的数据,然后返回结果ResultB到服务A
        3、服务A拿服务B返回的结果ResultB 在调用服务C,服务C也更新了10多个表,同时返回了结果ResultC给服务A
        4、服务A再次拿ResultC做为输入参数去调用服务D,服务D在处理业务逻辑的时候出现了异常,这时必须要回滚之前做的操作。(就算是使用异步步MQ机制,这种情况下仍然是要回滚的,改如何回滚呢?)

        这种情况下如何回滚服务B和服务C已经更新过的数据呢?涉及到那么多表,回滚也是相当困难,最要命的是可能已经有其他请求在服务B和服务C更新过的数据基础上做了业务处理,回滚的话会不会把别的线程写入的数据给覆盖掉?
        wwwwwzh:您好,请问下,您说的这种情况已经解决了么?是怎么解决的?
      • 匡和:有没有demo呢
      • WithLin:确实要根据业务来,是否一直重试,一般来讲3次就好了,重试3次不成功,那么就失败。再重新生产消息。
      • Jokerone_:有个问题。对订单,库存来说。比如只剩下一个库存了,如果订单生成成功了,但是扣减库存失败了。如果此时没有及时处理异常,再来一个订单依然可以生成成功(因为上一个库存扣减失败,商品仍有库存),但是其实这种情况已经导致超卖了(生成了两个订单,但只有一个库存)。麻烦问一下,对分布式事务来说,这样子有什么好的解决方式吗?
        b547ca0f2400:感觉实现起来还是有问题,用户提交订单后会先由订单业务来处理。然而就像上面说的要做到防超卖,首先要先执行扣减库存的操作,就扣减库存这一步,就要同步调用库存系统服务来实现扣减库存,而扣减库存这个操作并不在订单业务的本地事务中,这样又形成了一个分布式事务。
        Jokerone_:@jsondream 嗯嗯,了解了,谢谢
        jsondream:这个问题不属于分布式事务的问题,我可以吧他理解为业务梳理的问题,这种问题的防超卖一般的做法都是先执行不可能获取的条件,本例子中为,先执行扣减库存成功的操作,成功了才执行库存操作,扣减库存失败的话,根据业务进行处理
        这里我们提到了扣减库存成功的先决条件,这时候我们要保证的才是订单的生产结果的一致性,这时候我们可以做某些取舍,例如,回退库存,或者保证订单消费者生产成功。(通过是补偿机制去处理即可)
      • 铁汤:好文,不过这个要看具体的业务了
        jsondream:@铁汤 是的,所有没有业务支撑的方案都是白扯
      • wangen2009:哈哈哈 好

      本文标题:分布式事务之最终一致的Mq实现

      本文链接:https://www.haomeiwen.com/subject/xrwbjttx.html