分布式事务解决方案

分布式事务解决方案

分布式事务

什么是分布式事务

指一次大的操作由不同的小操作组成的,这些小的操作分布在不同的服务器上,分布式事务需要保证这些小操作要么全部成功,要么全部失败。从本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

典型的分布式事务场景:跨银行转操作就涉及调用两个异地银行服务

分布式产生的原因

1、数据库分表

当数据库单表数据达到千万级别,就要考虑分库分表,那么就会从原来的一个数据库变成多个数据库。例如如果一个操作即操作了01库,又操作了02库,而且又要保证数据的一致性,那么就要用到分布式事务。
20211122095726
2、应用SOA化

所谓的SOA化,就是业务的服务化。例如电商平台下单操作就会产生调用库存服务扣减库存和订单服务更新订单数据,那么就会设计到订单数据库和库存数据库,为了保证数据的一致性,就需要用到分布式事务。
20211122095745

总结:其实上面两种场景,归根到底是要操作多数据库,并且要保证数据的一致性,而产生的分布式事务的。

分布式事务的CAP理论

CAP理论:CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。

CAP原则是NOSQL数据库的基石。

分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:

一致性(C):数据在多个副本之间是否能够保持一致的特性。

可用性(A):是指系统提供的服务必须一致处于可用状态,对于每一个用户的请求总是在有限的时间内返回结果,超过时间就认为系统是不可用的

分区容错性(P):分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非整个网络环境都发生故障。
20211122100225

BASE理论:BASE是基本可用,软状态,最终一致性。是对CAP中一致性和可用性权限的结果,是基于CAP定理演化而来的,核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特定,采用适当的方式来使系统达到最终一致性

分布式4种常见解决方案

2PC提交

二阶段提交,是指将事务提交分成两个部分:准备阶段和提交阶段。事务的发起者称之为协调者,事务的执行者称为参与者。

阶段一:准备阶段

由协调者发起并传递带有事务信息的请求给各个参与者,询问是否可以提交事务,并等待返回结果。

个 参与者执行事务操作,将Undo和Redo放入事务日志中(但是不提交)

如果参与者执行成功就返回YES(可以提交事务),失败NO(不能提交事务)

阶段二:提交阶段

此阶段分两种情况:所有参与者均返回YES,有任何一个参与者返回NO

所有参与者均反馈YES时,即提交事务。

任何一个参与者反馈NO时,即中断事务。

3PC提交

3PC,三阶段提交协议,是2PC的改进版本,即将事务的提交过程分为CanCommit、PreCommit、do Commit三个阶段来进行处理。

阶段一:CanCommit

1、协调者向所有参与者发出包含事务内容的CanCommit请求,询问是否可以提交事务,并等待所有参与者答复。

2、参与者收到CanCommit请求后,如果认为可以执行事务操作,则反馈YES并进入预备状态,否则反馈NO。
阶段二:PreCommit

此阶段分为两种情况:

1.所有参与者均受到请求并返回YES。

2.有任何一个参与者返回NO,或者有任何一个参与者超时,协调者无法收到反馈,则事务中断

事务预提交:(所有参与者均反馈YES时)

1、协调者向所有参与者发出PreCommit请求,进入准备阶段。

2、参与者收到PreCommit请求后,执行事务操作,将Undo和Redo信息记入事务日志中(但不提交事务)。

3、各参与者向协调者反馈Ack响应或No响应,并等待最终指令。

中断事务:(任何一个参与者反馈NO,或者等待超时后协调者尚无法收到所有参与者的反馈时)

1、协调者向所有参与者发出abort请求。

2、无论收到协调者发出的abort请求,或者在等待协调者请求过程中出现超时,参与者均会中断事务。

阶段3:do Commit

此阶段也存在两种情况:

1、所有参与者均反馈Ack响应,即执行真正的事务提交。

2、任何一个参与者反馈NO,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务。

本地消息表(阿里Seata)

Seata的分布式事务解决方案是业务层面的解决方案,只依赖于单台数据库的事务能力。Seata框架中一个分布式事务包含3中角色:

Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。

Transaction Manager (TM): 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。

Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。

其中,TM是一个分布式事务的发起者和终结者,TC负责维护分布式事务的运行状态,而RM则负责本地事务的运行。如下图所示:

20211122102337

mq的ack手动补偿事务(rabbitmq)

RabbitMQ中与事务有关的主要有三个方法:

txSelect()

txCommit()

txRollback()

try {
            // 开启事务
            channel.txSelect();
            // 往队列中发出一条消息,使用rabbitmq默认交换机
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            // 提交事务
            channel.txCommit();
        } catch (Exception e) {
            e.printStackTrace();
            // 事务回滚
            channel.txRollback();
        }

消息生产者发送消息主要执行了四个步骤:

1:Client发送Tx.Select

2:Broker发送Tx.Select-Ok(在它之后,发送消息)

3:Client发送Tx.Commit

4:Broker发送Tx.Commit-Ok

消费者手动ack确认

// 关闭自动确认
  channel.basicConsume(QUEUE_NAME, false, queueingConsumer1);
 //手动确认ack  是否批量处理.true:将一次性ack所有小于deliveryTag的消息
 channel.basicAck(delivery1.getEnvelope().getDeliveryTag(), false);

  public static void main(String[] args)  throws Exception {
       Connection connection = RabbitConnectionUtil.getConnection("127.0.0.1", 5672, "/", "guest", "guest");
       Channel channel = connection.createChannel();
       //3.声明队列
       channel.queueDeclare(QUEUE_NAME, false, false, false, null);
       //设置每次从队列获取消息的数量 能者多劳的原则不至于做的快的做完了就歇着了!
       channel.basicQos(1);
       //4.定义队列的消费者
       QueueingConsumer queueingConsumer1 = new QueueingConsumer(channel);
        /*
    true:表示自动确认,只要消息从队列中获取,无论消费者获取到消息后是否成功消费,都会认为消息成功消费.
    false:表示手动确认,消费者获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,
    如果消费者一直没有反馈,那么该消息将一直处于不可用状态,并且服务器会认为该消费者已经挂掉,不会再给其发送消息,
    直到该消费者反馈.例如:channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
     */
       //b:ack
       channel.basicConsume(QUEUE_NAME, false, queueingConsumer1);
       //6.获取消息
       while (true) {
           anInt += 1;
           QueueingConsumer.Delivery delivery1 = queueingConsumer1.nextDelivery(); 
           String message1 = String.valueOf(delivery1.getBody());          
           System.out.println("[" + String.valueOf(anInt) + "]:receve msg:" + message1);
           //手动确认ack  是否批量处理.true:将一次性ack所有小于deliveryTag的消息
           channel.basicAck(delivery1.getEnvelope().getDeliveryTag(), false);
           // System.out.println("[x] Received '" + message2 + "'");
           Thread.sleep(100);
       }
   }

福禄·研发中心 福小雄
原文地址:https://www.cnblogs.com/fulu/p/15620497.html