分布式事务 CAP 理解论证 解决方案

前言
在大型系统架构演变中,当前下,分布式是一个必然的选择,分布式事务是绕不开的一个点.spring

目录
概述
论证
解决方案
3.1 维护本地消息表
3.2 使用rocketmq事务消息
3.3 两阶段提交协议(2PC)
3.4 TCC事务补偿机制
正文
1.概述
在单体架构中,咱们的事务能够经过数据库的ACID来操做,不会出现什么问题.数据库

1.1 问题描述:
但随着规模扩大,咱们的逻辑服务进行拆分A,B,C…模块,部署在多台服务器,数据库通常也是多台,进行了分库分表等操做,这些A,B,C…模块间经过网络通讯完成协做,那此刻就产生了单体应用触发不了的问题:浏览器

一致性问题: 既然是多个数据库,那么经过网络操做,客观上就会存在延时(短距离很小,当跨国时就很慢了)甚至不可达,
可用性问题: 用户访问某个页面,应该给提供可预期的结果,而不是浏览器报错,404,500,页面丢失…等等,能够经过一些策略完成
网络分区:在分布式场景中,不可避免的会出现多个模块系统协同工做,根据墨菲定律,就必定有概率发生网络中断,    延时,不可达等客观存在问题
产生的问题不止这三种,但其余的都是可处理的问题,这三种是大头
注意误区: 由于先有分区的存在,才致使了一致性和可用性问题缓存

1.2 CAP 理论:
而对于分布式中的问题的解决方案,CAP原则出现,描述以下:springboot

一致性(Consistency):
像A节点写入一条信息以后,同一时刻,在其余节点均可以读到这条信息服务器

可用性(Availability):
多布一些节点A,B,C…,任什么时候刻,用户访问,都应该以可预期的结果返回,而不是浏览器报错,404,500,页面丢失…等用户体验很差的状况发生网络

分区容忍性(PartitionTolerance):
当各系统模块间通讯出现问题时,设计一个策略,使系统仍可对外提供知足一致性或可用性架构

刚接触cap时,有些不理解分区容忍性,咱们本身倒推一下:并发

为了保证一致性,咱们须要各个节点同步消息
为了保证可用性咱们能够多部署节点,部分节点挂了仍可对外提供服务
为了保证分区容忍性:此刻卡壳了,怎么作?没了一种具体的方式,然而他仍是客观存在的
后来发现:进入了思惟盲点:只要在分布式场景中,分区必然存在,那么若是不处理分区发生时的状况,节点没法通信时会发生什么?–此刻若是仍对外提供服务,那么致使没法同步消息,即保证不了强一致性;若是要保证强一致性,那么就须要节点阻塞,一直等待通信恢复,即保证不了可用性.
因此分区容忍性就是:当发生分区问题时,咱们使用策略,在一致性和可用性两者间选择
注意: 没法通讯包括网络问题,或者节点机器宕机
误区: CAP理论中说三者不可兼得,但实际状况是,在分布式场景中分区必定存在,即必须有分区容忍性对应的策略,以后才能在一致性和可用性间两者之间选择.因此对主流架构来讲不是三选二,而是二选一分布式

1.3 三种组合:
分布式系统中,为了提升强一致性,应该使用更少的节点,这样更容易同步消息,而为了提升可用性,不得不增长节点,保证高可用,矛盾,因此须要取舍:

CA:
保证可用性和一致性,放弃分区:除非不是分布式架构,或者应用在一个永不会通讯故障的网络中(理想),只有个别场景符合,当前的互联网架构显然不符合使用
CP:
保证一致性和分区容忍性,放弃可用性:当节点间不可通讯时,进行阻塞,直到通讯恢复,期间没法再对外提供服务,用户体验很差,如A转帐给B,只有A扣款成功并B收款成功,整个事务才算完成,显然耗费资源
AP:
保证可用性和分区容忍性,放弃强一致性(使用最终一致性):给出一个用户能够忍受的时间,时间内达成数据的最终一致性,好比跨行转帐,并非马上到帐,多是明天,或者2小时内到帐

2 论证
操做: 在北京和上海各有一个节点Node1和Node2,咱们对Node1插入一条数据
看到这里,就假定默认选择了分区容忍性,在此基础上抉择A和C:

2.1在网络稳定时:
向Node1插入一条数据,数据能够同步到Node2,在北京和上海访问,没什么区别


2.2在网络中断时:
向Node1插入一条数据,Node1在向Node2同步时,网络中断,假如北京和上海的用户是访问的两个节点,此刻你刚插入 的数据在上海是访问不到的,此刻就要进行抉择:
1.保留强一致性:节点1同步到节点2以前,就不显示插入成功,一直阻塞,直到同步成功,
2.保留可用性:向节点1插入一条消息后,用一条消息通知节点2(这个消息要确保成功),此时给用户返回插入成功,等网络恢复后,执行这条消息便可;除非特大天然灾害,通常的网络问题均可以短期恢复.


可根据各自的业务场景,选择对应的策略
注意: 随着规模的继续扩大,节点更多,咱们维持一致性的成本更高

3 解决方案
3.1 维护本地消息表
分布式事务就是跨多个系统的事务,能够拆分为多个子系统的本地事务;
分布式事务=A系统本地事务 + B系统本地事务 + 消息通知;
准备: A系统维护一张消息表log1,状态为未执行,B系统维护2张表,未完成表log2,已完成表log3,消息中间件用两个topic,topic1是A系统通知B要执行任务了,topic2是B系统通知A已经完成任务了,

1.用户在A系统里领取优惠券,并往log1插入一条记录
2.由定时任务轮询log1,发消息给B系统
3.B系统收到消息后,先检查是否在log3中执行过这条消息,没有的话插入log2表,并进行发短信,发送成功后删除log2的记录,插入log3
4.B系统发消息给A系统
5.A系统根据id删除这个消息

咱们假设网络中断:
1.在1处中断:此时咱们插入优惠券和log1用的本地事务,即便发消息失败,有定时任务轮询,会再次发送
2.在2处中断:当B系统发短信后,通知A系统失败,由于A系统有定时任务轮询,会重复再发一次,因此B系统会先检查log3,若是已经执行过了,就不发短信了,再次给A系统发送执行完成的消息,

实现最终事务一致要求:
预留资源成功理论上要求正式执行成功,若是执行失败会进行重试,要求业务执行方法实现幂等,每次执行的结果不变;
**优势:**开发简单,mq性能较高
**缺点:**业务耦合,由于频繁轮询数据库,增大了数据库负载,此时数据库的性能瓶颈尤其明显,不适合大高并发场景,中等的规模仍是能够知足的

3.2 使用rocketmq事务消息
阿里巴巴的rocketmq支持事务消息,实现机制,能够参考另外一篇博客:https://blog.csdn.net/weixin_40533111/article/details/84451219

3.3 两阶段提交协议(2PC)
为解决分布式系统的数据一致性问题出现了两阶段提交协议(2 Phase Commitment Protocol),两阶段提交由
协调者和参与者组成,共通过两个阶段和三个操做,部分关系数据库如Oracle、MySQL支持两阶段提交协议.
流程图:

1)第一阶段:准备阶段(prepare)
协调者通知参与者准备提交订单,参与者开始投票。
协调者完成准备工做向协调者回应Yes。
2)第二阶段:提交(commit)/回滚(rollback)阶段
协调者根据参与者的投票结果发起最终的提交指令。
若是有参与者没有准备好则发起回滚指令。
一个下单减库存的例子:


一、应用程序链接两个数据源。
二、应用程序经过事务协调器向两个库发起prepare,两个数据库收到消息分别执行本地事务(记录日志),但不提
交,若是执行成功则回复yes,不然回复no。
三、事务协调器收到回复,只要有一方回复no则分别向参与者发起回滚事务,参与者开始回滚事务。
四、事务协调器收到回复,所有回复yes,此时向参与者发起提交事务。若是参与者有一方提交事务失败则由事务协
调器发起回滚事务。
2PC的优势:实现强一致性,部分关系数据库支持(Oracle、MySQL等)。
缺点:整个事务的执行须要由协调者在多个节点之间去协调,增长了事务的执行时间,性能低下。
解决方案有:springboot+Atomikos or Bitronix
3PC主要是解决协调者与参与者通讯阻塞问题而产生的,它比2PC传递的消息还要多,性能不高。详细参考3PC:

3.4 TCC事务补偿机制
TCC事务补偿是基于2PC实现的业务层事务控制方案,它是Try、Confirm和Cancel三个单词的首字母,含义以下:

Try 检查及预留业务资源
完成提交事务前的检查,并预留好资源。
Confirm 肯定执行业务操做
对try阶段预留的资源正式执行。
Cancel 取消执行业务操做
对try阶段预留的资源释放。
示例:

Try 下单业务由订单服务和库存服务协同完成,在try阶段订单服务和库存服务完成检查和预留资源。 订单服务检查当前是否知足提交订单的条件(好比:当前存在未完成订单的不容许提交新订单)。 库存服务检查当前是否有充足的库存,并锁定资源。 Confirm 订单服务和库存服务成功完成Try后开始正式执行资源操做。 订单服务向订单写一条订单信息。 库存服务减去库存。 Cancel 若是订单服务和库存服务有一方出现失败则所有取消操做。 订单服务须要删除新增的订单信息。 库存服务将减去的库存再还原。 优势:最终保证数据的一致性,在业务层实现事务控制,灵活性好。XA两阶段提交资源层面的,而TCC实际上把资源层面二阶段提交上提到了业务层面来实现。有效了的避免了XA两阶段提交占用资源锁时间过长致使的性能地下问题。 缺点:开发成本高,每一个事务操做每一个参与者都须要实现try/confirm/cancel三个接口。 注意:TCC的try/confirm/cancel接口都要实现幂等性,在为在try、confirm、cancel失败后要不断重试;它让多个系统保证了原子性操做,所以成本仍是比较高的。 幂等性: 幂等性是指同一个操做不管请求多少次,其结果都相同。 幂等操做实现方式有: 一、操做以前在业务方法进行判断若是执行过了就再也不执行。 二、缓存全部请求和处理的结果,已经处理的请求则直接返回结果 三、在数据库表中加一个状态字段(未处理,已处理),数据操做时判断未处理时再处理。

相关文章
相关标签/搜索