zoukankan      html  css  js  c++  java
  • 分布式事务

    一、两阶段提交 2PC

     

    1、数据库层面的两阶段提交(XA trainsaction)

     两阶段提交是目前经常使用的一种解决分布式事物的强一致性的一种方案,它是由事物协调器和若干个事物执行者(数据库本身)组成。

      所谓两阶段提交就是将每一个事物拆分成 任务+提交,将任务的执行和任务提交分成两步。当客户端发起事物执行请求时,事物协调器会通知各个数据库执行任务,

    但是不提交,执行成功后,各个数据库向事物协调器发一个是否 执行成功的通知,各个数据库都发送成功通知后,事物协调器就会向各个数据库执行提交的通知。

      由此可以看出,当各个数据库执行完任务(未提交),每个数据库的回话并没有结束,直到发出commit通知后,才会结束,

    这就会导致大量的数据库连接发生堵塞,所以会在高并发的情景下极大的影响效率。

     

     

    2、业务层面的两阶段提交(TCC补偿型事物)

      TCC补偿型事物是对数据库的两阶段提交的一种延伸,将传统两阶段提交在数据库 转移到业务方面,解决了两阶段提交带来了数据库阻塞,性能差的问题,

    是一种目前经常使用的来保持较强一致性的模式。

      Tcc(try-commit-canel 三个阶段)

    try阶段:各个事物执行业务逻辑,并向协调器发送是否执行成功的通知

    commit/canel:协调器根据业务的执行情况,来进行统一的提交或者回退(在项目中发起事物的服务相当于协调器,来协调各从服务)

    tcc事物补偿模式符合对即时性和一致性比较高的系统,但是代码的侵入性比较强,需要针对每个事物写回滚操作,可能就需要很多的ifelse嵌套这种模式也意味着需要主从服务之间的通信,这种通信也必然导致rpc调用次数的增多,从而影响性能。根据业务来看这个性能,看是否可以接受。

     

     

    二、基于消息达到最终一致性(异步确保)

     

     

    通过消息中间件,来保证分布式事物的最终一致性。这是一种比较提倡的解决方案,有利于服务之间解耦,虽然是异步的,但基本上事物也是即时性的。

    方式:

      

          将业务A的提交操作和发送消息的记录 写在同一个事物中(把消息发送记录在本地库中),然后业务B接收到消息之后进行相应的操作,成功后,向A发送成功通知。失败则通知A,进行回滚。

    A中的消息发送不成功的时候,则会通过定时任务轮训来进行发送,从而保证消息最终肯定可以到达B

      但是当A接收到B的成功通知后 应该删除消息记录或者修改记录状态,但是突然A挂掉了,A就没有及时修改本地消息记录,导致定时轮训的时候会重发,如果此时B中业务不是幂等的则会出现问题,所以如何避免这种消息重发呢? 在B方也对消息进行记录,当A中发送过来消息后,B判断消息是否执行过,来避免这种问题

     

    三、事务消息(RocketMQ)

    事务消息需要消息队列提供相应的事物功能,目前kafka和rocket提供了事务相关功能。

    在我理解:事务消息只保证了生产者系统和消息队列的事务性,发送消息失败以及生产者业务系统失败都会进行回滚,不会去考虑消费者

    拿订单系统流程来说,由于清空购物车功能非生成订单时的必需功能,所以在生成订单时异步清空购物车

    1、订单系统在消息系统开启事务

    2、订单系统发送半消息到消息队列。 半消息:不是指一半的消息,而是完整的消息,只不过在生产者事务未提交前,对消费者来说是不可见的。

    3、订单系统执行本地事务,创建订单

    4、根据本地事务的执行结果决定提交或者回滚事务消息。如果订单创建成功,那就提交事务消息,购物车系统就可以消费到这条消息继续后续的流程。

    如果订单创建失败,那就回滚事务消息,购物车系统就不会收到这条消息

     

    RocketMQ 增加了事务反查机制来解决事务提交失败的问题:

    反查接口的定义,它检查的是本地事务(在我们这个例子里面就是数据库事务)有没有执行成功,并不比较数据是否一致

    如果 Producer 也就是订单系统,在提交或者回滚事务消息时发生网络异常,RocketMQ 的 Broker 没有收到提交或者回滚的请求,Broker 会定期去 Producer 上反查这个事务对应的本地事务的状态,然后根据反查结果决定提交或者回滚这个事务。

    为了支撑这个事务反查机制,我们的业务代码需要实现一个反查本地事务状态的接口,告知 RocketMQ 本地事务是成功还是失败。

    具体流程如下图:

    有如下几个疑问:

     

    1,为何不在订单系统提交成功后再发送消息?

      1,订单系统提交成功后断电或者网络波动,导致发送消息失败,不能保证其事务性!RocketMQ采用类似两段提交的方式,发送半消息目的也是为了让RocketMQ进行事务反查。

      2,将发送消息从订单系统事务中拿出来,也提高了订单系统的性能,提高了吞吐量

    2, 订单事务和消息都发送成功了,清空购物车失败了怎么办?

      事务消息机制不支持消费者失败后的回滚,如果想要支持则选择其他类型分布式事务

    3,此种方式高度依赖mq,mq挂了怎么办?  

         mq的broker采用高可用集群部署

     

     

     

     

  • 相关阅读:
    mysql 从库执行insert失败导致同步停止
    MySQL 占用cpu 100%
    MySQl 主从配置实战
    tomcat 线程数与 mysql 连接数综合调优
    mysql 数据迁移
    Windows系统上设置 Git Bash 的 Font 及 Locale
    java -jar 使用要点
    ConcurrentHashMap 从Java7 到 Java8的改变
    sql索引组织
    注册、启动、停止windows服务
  • 原文地址:https://www.cnblogs.com/volare/p/12264503.html
Copyright © 2011-2022 走看看