事务、分布式事务解决方案、CAP定理和BASE理论


说明

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,所以产生了微服务、DDD等

  1. 既然是分布式,项目的高可用就离不开集群。集群的一致性,可用性,分区容错就有CAP定理
  2. 在CAP定理上又衍生了BASE理论
  3. 在单服务中的本地事务和微服务的远程调用产生的分布式事务
  4. 事务就离不开事务数据库的支持,事务的ACID
  5. 本地事务,在分布式系统,只能控制住自己的回滚,控制不了其他服务的回滚
  6. 分布式事务: 最大原因。网络问题+分布式机器
  7. 分布式事务离不开本地事务,分布式事务的解决方案的归纳

一、本地事务

1.1、事务特性ACID

数据库事务的几个特性:原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)
和持久性(Durabilily),简称就是 ACID

  1. 原子性(Atomicity ):一系列的操作整体不可拆分,要么同时成功,要么同时失败
  2. 一致性(Consistency ):数据在事务的前后,业务整体一致
  3. 隔离性(Isolation):事务之间互相隔离。
  4. 持久性(Durabilily):一旦事务成功,数据一定会落盘在数据库。

(a)单体应用中,我们多个业务操作使用同一条连接操作不同的数据表,一旦有异常,我们可以很容易的整体回滚;
(b)比如买东西业务,扣库存,下订单,账户扣款,是一个整体;必须同时成功或者失败
(c)一个事务开始,代表以下的所有操作都在同一个连接里面

1.2、 事务的隔离级别

  1. READ UNCOMMITTED(读未提交)
    该隔离级别的事务会读到其它未提交事务的数据,此现象也称之为脏读

  2. READ COMMITTED(读提交)
    (a)一个事务可以读取另一个已提交的事务,多次读取会造成不一样的结果,此现象称为不可重复读问题
    (b)Oracle 和 SQL Server 的默认隔离级

  3. REPEATABLE READ(可重复读)
    (a)该隔离级别是 MySQL 默认的隔离级别,在同一个事务里,select 的结果是事务开始时时间点的状态,因此,同样的 select 操作读到的结果会是一致的,但是,会有幻读现象。
    (b)MySQL的 InnoDB 引擎可以通过 next-key locks 机制(参考下文"行锁的算法"一节)来避免幻读
    (c)简单的理解就是你读了一个数据是100,数据被改其他事务改成了200,你这次的事务多次读取这个值也还是100,这就是可重复读

  4. SERIALIZABLE(序列化)
    (a)在该隔离级别下事务都是串行顺序执行的,MySQL 数据库的 InnoDB 引擎会给读操作隐式加一把读共享锁,从而避免了脏读、不可重读复读和幻读问题
    (b)但是性能会严重下降

1.3、 事务的7种传播行为

  1. PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置

  2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行

  3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

  4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务

  5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

  6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

  7. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

1.3.1、 springboot的使用

  1. spring默认使用jdk自动生成的按接口的动态代理
  2. 在springboot中使用,采用注解@Transactional
  3. 推荐进入AOP,采用aspectj 动态代理更加强大
  4. 事务的自动配置TransactionAutoConfiguration

重点关注2种

(a)Propagation.REQUIRED :默认也是最常用的设置
(b)Propagation.REQUIRES_NEW :创建新事务

1.3.1.1、同一个service里面调用

  1. 同一个对象内事务方法互调默认失效,原因 绕过了代理对象
  2. 事务使用代理对象来控制的

public class OrderServiceImpl  implements OrderService {
	/**
     * @Transactional(isolation = Isolation.READ_COMMITTED[事务的隔离级别], 
     *                propagation = Propagation.REQUIRED[传播行为],
     *                timeout = 2[超时时间]
     *                )
     */


	//a事务的所有设置就传播到了和他公用一个事务的方法
    @Transactional(timeout = 30) 
    public void a() {
        //b,c做任何设置都没用。都是和a公用一个事务
        this.b();// 没用
        this.c(); //没用

       /**
      	 * 这是才用aspectj获取他的动态代理对象,这样使用的话,b和c的配置就能生效
      	 * 当然采用注入自己+@lazy的方式也是可以做到的
         * OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
         * orderService.b();//生效
         * orderService.c();//生效
         */
        int i = 10 / 0;
    }

    @Transactional(propagation = Propagation.REQUIRED, timeout = 2)//默认设置,2秒超时
    public void b() {
        //结果是30s
        // ...业务
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, timeout = 20)//创建新事务,20秒超时
    public void c() {
		//结果是30s
		// ...业务
    }
}

1.3.1.2、不同service里面调用

伪代码演示

//第1个sevice
public class  AService {
 	@Autowired
 	BService cService;
 	@Autowired
 	CService cService;
 	

	@Transactional(timeout = 30) 
    public void a() {
        bService.b(); //会跟a事务一样
        cService.c(); //创建出新事务(不回滚)
        int i = 10 / 0;
    }
}


//第2个sevice
public class  BService {

	@Transactional(propagation = Propagation.REQUIRED, timeout = 2)
	 public void b() {
	 		//因为是Propagation.REQUIRED,所以跟Aservice的方法a事务一样,会30秒超时,会回滚
			// ...业务
       
    }
	
}

//第3个sevice
public class  CService {

	 @Transactional(propagation = Propagation.REQUIRES_NEW, timeout = 20)
	 public void c() {
	 		//因为是Propagation.REQUIRES_NEW,所以是新事物,不会回滚,20秒超时
			// ...业务
	 }
	
}


1.3.2、如何切换成aspectj动态代理

  1. 引入 spring-boot-starter-aop
<dependency>
 	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

在启动类使用注解

  1. @EnableTransactionManagement(proxyTargetClass = true)//启动事务管理
  2. @EnableAspectJAutoProxy(exposeProxy=true)//开启aspectj动态代理

(a)在同一个类里面,编写两个方法,内部调用的时候,会导致事务设置失效。原因是没有用到代理对象的缘故。
(b)所以用AopContext.currentProxy() 调用方法会解决


二、CAP 定理与 BASE 理论

1.1、CAP 定理

1.1.1、CAP 定理说明与图解

1、CAP 原则又称 CAP 定理,指的是在一个分布式系统的一致性、可用性、分区容错性
2、 CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾

  1. 一致性(Consistency)
    在分布式系统中的所有数据备份,在同一时刻是否同样的值。
    (等同于所有节点访 问同一份最新的数据副本)

  2. 可用性(Availability)
    在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。
    (对数据更新具备高可用性)

  3. 分区容错性(Partition tolerance)
    (a)大多数分布式系统都分布在多个子网络
    (b) 每个子网络就叫做一个区(partition)
    © 分区容错的意思是,区间通信可能失败。比如,一台服务器放在上海,另一台服务器放在北京,这就是两个区,它们之间可能无法通信

一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们,剩下的 C 和 A 无法同时做

如图:
在这里插入图片描述

1.1.2、分布式系统中实现一致性的 算法

  1. raft 算法 ==>官网算法动画演示地址
  2. paxos算法

介绍下raft 算法保持一致性的算法

在raft 算法中有3中状态

  1. 随从 The Follower state
  2. 候选者 the Candidate state
  3. 领导 the Leader state

1、选举过程:

采用投票的方式,选出领导
在这里插入图片描述
2、 数据同步:

采用日志同步的方式,要保持心跳
在这里插入图片描述
3、5个节点网络中断暂时被分区:

在这里插入图片描述
4、节点个数不能是偶数,不然可以会出现得票相同,无法选举出领导的情况,导致集群存在,但是无法工作进行数据同步

1.2、为什么会产生base理论

  1. 对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,所以节点故障、网络故障是常态.
  2. 而且要保证服务可用性达到 99.99999%(N 个 9),即保证P 和 A,舍弃 C
  3. 所以base理论产生,即使无法做到强一致性(CAP 的一致性就是强一致性),但可 以采用适当的采取弱一致性,即最终一致性

1.3、BASE理论

BASE理论是对 CAP 理论的延伸,思想是即使无法做到强一致性(CAP 的一致性就是强一致性),但可 以采用适当的采取弱一致性,即最终一致性

1.3.1、BASE含意

  1. 基本可用(Basically Available)
    (a)、基本可用是指分布式系统在出现故障的时候,允许损失部分可用性(例如响应时间、功能上的可用性),允许损失部分可用性。需要注意的是,基本可用绝不等价于系统不可用。

    (b)、响应时间上的损失:正常情况下搜索引擎需要在 0.5 秒之内返回给用户相应的查询结果,但由于出现故障(比如系统部分机房发生断电或断网故障),查询结果的响应时间增加到了 1~2 秒。
    ©、功能上的损失:购物网站在购物高峰(如双十一)时,为了保护系统的稳定性,部分消费者可能会被引导到一个降级页面

  2. 软状态( Soft State)
    (a)、软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。
    (b)、分布式存储中一般一份数据会有多个副本,允许不同副本同步的延时就是软状态的体现。
    ©、mysql replication 的异步复制也是一种体现。


  3. 最终一致性( Eventual Consistency)
    (a)、最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。
    (b)、弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

1.3.2、强一致性、弱一致性、最终一致性

  1. 从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。
  2. 对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性
  3. 如果能容忍后续的部分或者全部访问不到,则是弱一致性
  4. 或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性

三、分布式事务解决方案

1.1、解决方案归纳

  1. 2PC 模式(基本不用)
  2. 3PC模式(基本不用)
  3. TCC补偿模式(自己业务拆分实现)
  4. 数据库的时候也是日志+数据来实现的,所以将业务的执行和将消息放入消息表中的操作放在同一个事务中
  5. 消息事务(结合消息队列)
  6. 阿里巴巴提供的seata。但是seate的入侵性比较强,对性能方面的把控需要有较强的解决能力

1.2、2PC 模式

  1. 数据库支持的 2PC【2 phase commit 二阶提交】,又叫做 XA Transactions
  2. MySQL 从 5.5 版本开始支持,SQL Server 2005 开始支持,Oracle 7 开始支持

其中,XA 是一个两阶段提交协议,该协议分为以下两个阶段:

  1. 第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
  2. 第二阶段:事务协调器要求每个数据库提交数据。
  3. 其中,如果有任何一个数据库否决此次提交,那么所有数据库都会被要求回滚它们在此事务中的那部分信息。

在这里插入图片描述
在这里插入图片描述

但是不推荐原因:

  1. XA 协议比较简单,而且一旦商业数据库实现了 XA 协议,使用分布式事务的成本也比较低。
  2. XA 性能不理想,特别是在交易下单链路,往往并发量很高,XA 无法满足高并发
  3. XA 目前在商业数据库支持的比较理想,在 mysql 数据库中支持的不太理想,mysql 的XA 实现,没有记录 prepare 阶段日志,主备切换回导致主库与备库数据不一致。
  4. 许多 nosql 也没有支持 XA,这让 XA 的应用场景变得非
  5. 也有 3PC,引入了超时机制(无论协调者还是参与者,在向对方发送请求后,若长时间未收到回应则做出相应处理)

3PC图解:
在这里插入图片描述

2PC 和 3PC 是一种强一致性事务,不过还是有数据不一致,阻塞等风险,而且只能用在数据库层面

1.3、TCC 事务补偿型方案-柔性事务

1.3.1、什么是刚性事务和柔性事务

  1. 刚性事务:遵循 ACID 原则,强一致性
  2. 柔性事务:遵循 BASE 理论,最终一致性
  3. 与刚性事务不同,柔性事务允许一定时间内,不同节点的数据不一致,但要求最终一致。

1.3.2、TCC说明

什么是TCC就是Try(预留和锁定资源)、Confirm(提交)、Cancel(回滚)自己在业务中拆分,拆出这3个模块

在这里插入图片描述

  1. 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
  2. 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
  3. 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。

所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中
在这里插入图片描述
注意事项

  1. TCC 对业务的侵入较大和业务紧耦合,需要根据特定的场景和业务逻辑来设计相应的操作
  2. 撤销和确认操作的执行可能需要重试,因此还需要保证操作的幂等性

相对于 2PC、3PC ,TCC 适用的范围更大,但是开发量也更大,毕竟都在业务上实现,而且有时候你会发现这三个方法还真不好写。
不过也因为是在业务上实现的,所以TCC可以跨数据库、跨不同的业务系统来实现事务。

1.4、消息事务

在这里插入图片描述

1.4.1、最大努力通知型方案-柔性事务

也就是整合mq消息队列进行实现

  1. 按规律进行通知,不保证数据一定能通知成功,但会提供可查询操作接口进行核对。
  2. 这种方案主要用在与第三方系统通讯时,比如:调用微信或支付宝支付后的支付结果通知。
  3. 这种方案也是结合 MQ 进行实现,例如:通过 MQ 发送 http 请求,设置最大通知次数。达到通
    知次数后即不再通知

1.4.2、柔性事务-可靠消息+最终一致性方案(异步确保型)

所谓的可靠消息是指消息的手动签收,保证消息的可靠性

  1. 业务处理服务在业务事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不是真正的发送。
  2. 业务处理服务在业务事务提交之后,向实时消息服务确认发送
  3. 只有在得到确认发送指令后,实时消息服务才会真正发送
  4. 防止消息丢失:开启mq的ack模式
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

suqinyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值