CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性)这3个基本需求,最多只能同时满足其中的2个。
CAP原则
选项描述Consistency(一致性)指数据在多个副本之间能够保持一致的特性(严格的一致性)Availability(可用性)指系统提供的服务必须一直处于可用的状态,每次请求都能获取到非错的响应(不保证获取的数据为最新数据)Partition tolerance(分区容错性)分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障
2. 为什么CAP不可兼得呢?首先对于分布式系统,分区是必然存在的,所谓分区指的是分布式系统可能出现的字区域网络不通,成为孤立区域的的情况。
分区
那么分区容错性(P)就必须要满足,因为如果要牺牲分区容错性,就得把服务和资源放到一个机器,或者一个“同生共死”的集群,那就违背了分布式的初衷。
那么满足分区容错的基础上,能不能同时满足一致性和可用性?
假如现在有两个分区N1和N2,N1和N2分别有不同的分区存储D1和D2,以及不同的服务S1和S2。
分区的服务
假如现在有这样的场景:
接下来:
所以,可以看出,分区容错的前提下,一致性和可用性是矛盾的。
3. CAP对应的模型和应用?CA without P
理论上放弃P(分区容错性),则C(强一致性)和A(可用性)是可以保证的。实际上分区是不可避免的,严格上CA指的是允许分区后各子系统依然保持CA。
CA模型的常见应用:
CP without A
放弃A(可用),相当于每个请求都需要在Server之间强一致,而P(分区)会导致同步时间无限延长,如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。
CP模型的常见应用:
AP wihtout C
要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。
AP模型常见应用:
举个大家更熟悉的例子,像我们熟悉的注册中心ZooKeeper、Eureka、Nacos中:
BASE(Basically Available、Soft state、Eventual consistency)是基于CAP理论逐步演化而来的,核心思想是即便不能达到强一致性(Strong consistency),也可以根据应用特点采用适当的方式来达到最终一致性(Eventual consistency)的效果。
BASE的主要含义:
什么是基本可用呢?假设系统出现了不可预知的故障,但还是能用,只是相比较正常的系统而言,可能会有响应时间上的损失,或者功能上的降级。
什么是硬状态呢?要求多个节点的数据副本都是一致的,这是一种“硬状态”。
软状态也称为弱状态,相比较硬状态而言,允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。
上面说了软状态,但是不应该一直都是软状态。在一定时间后,应该到达一个最终的状态,保证所有副本保持数据一致性,从而达到数据的最终一致性。这个时间取决于网络延时、系统负载、数据复制方案设计等等因素。
分布式锁单体时代,可以直接用本地锁来实现对竞争资源的加锁,分布式环境下就要用到分布式锁了。
5. 有哪些分布式锁的实现方案呢?常见的分布式锁实现方案有三种:MySQL分布式锁、ZooKepper分布式锁、Redis分布式锁。
分布式锁
5.1 MySQL分布式锁如何实现呢?用数据库实现分布式锁比较简单,就是创建一张锁表,数据库对字段作唯一性约束。
加锁的时候,在锁表中增加一条记录即可;释放锁的时候删除记录就行。
如果有并发请求同时提交到数据库,数据库会保证只有一个请求能够得到锁。
这种属于数据库 IO 操作,效率不高,而且频繁操作会增大数据库的开销,因此这种方式在高并发、高性能的场景中用的不多。
5.2 ZooKeeper如何实现分布式锁?ZooKeeper也是常见分布式锁实现方法。
ZooKeeper的数据节点和文件目录类似,例如有一个lock节点,在此节点下建立子节点是可以保证先后顺序的,即便是两个进程同时申请新建节点,也会按照先后顺序建立两个节点。
ZooKeeper如何实现分布式锁
所以我们可以用此特性实现分布式锁。以某个资源为目录,然后这个目录下面的节点就是我们需要获取锁的客户端,每个服务在目录下创建节点,如果它的节点,序号在目录下最小,那么就获取到锁,否则等待。释放锁,就是删除服务创建的节点。
ZK实际上是一个比较重的分布式组件,实际上应用没那么多了,所以用ZK实现分布式锁,其实相对也比较少。
5.3 Redis怎么实现分布式锁?Redis实现分布式锁,是当前应用最广泛的分布式锁实现方式。
Redis执行命令是单线程的,Redis实现分布式锁就是利用这个特性。
实现分布式锁最简单的一个命令:setNx(set if not exist),如果不存在则更新:
setNx resourceName value
加锁了之后如果机器宕机,那我这个锁就无法释放,所以需要加入过期时间,而且过期时间需要和setNx同一个原子操作,在Redis2.8之前需要用lua脚本,但是redis2.8之后redis支持nx和ex操作是同一原子操作。
set resourceName value ex 5 nx
当然,一般生产中都是使用Redission客户端,非常良好地封装了分布式锁的api,而且支持RedLock。
分布式事务6.什么是分布式事务?分布式事务是相对本地事务而言的,对于本地事务,利用数据库本身的事务机制,就可以保证事务的ACID特性。
ACID
而在分布式环境下,会涉及到多个数据库。
多数据库
分布式事务其实就是将对同一库事务的概念扩大到了对多个库的事务。目的是为了保证分布式系统中的数据一致性。
分布式事务处理的关键是:
分布式常见的实现方案有 2PC、3PC、TCC、本地消息表、MQ消息事务、最大努力通知、SAGA事务 等等。
7.1 说说2PC两阶段提交?说到2PC,就不得先说分布式事务中的 XA 协议。
在这个协议里,有三个角色:
XA协议
XA协议采用两阶段提交方式来管理分布式事务。XA接口提供资源管理器与事务管理器之间进行通信的标准接口。
两阶段提交的思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情况决定各参与者是否要提交操作还是回滚操作。
2PC
优点:尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于MySQL是从5.5开始支持。
缺点:
三阶段提交(3PC)是二阶段提交(2PC)的一种改进版本 ,为解决两阶段提交协议的单点故障和同步阻塞问题。
三阶段提交有这么三个阶段:CanCommit,PreCommit,DoCommit三个阶段
3PC
可以看出,三阶段提交解决的只是两阶段提交中单体故障和同步阻塞的问题,因为加入了超时机制,这里的超时的机制作用于 预提交阶段 和 提交阶段。如果等待 预提交请求 超时,参与者直接回到准备阶段之前。如果等到提交请求超时,那参与者就会提交事务了。
无论是2PC还是3PC都不能保证分布式系统中的数据100%一致。
7.3 TCC了解吗?TCC(Try Confirm Cancel) ,是两阶段提交的一个变种,针对每个操作,都需要有一个其对应的确认和取消操作,当操作成功时调用确认操作,当操作失败时调用取消操作,类似于二阶段提交,只不过是这里的提交和回滚是针对业务上的,所以基于TCC实现的分布式事务也可以看做是对业务的一种补偿机制。
TCC下单减库存
TCC 是业务层面的分布式事务,保证最终一致性,不会一直持有资源的锁。
本地消息表的核心思想是将分布式事务拆分成本地事务进行处理。
例如,可以在订单库新增一个消息表,将新增订单和新增消息放到一个事务里完成,然后通过轮询的方式去查询消息表,将消息推送到MQ,库存服务去消费MQ。
本地消息表
执行流程:
订单服务中的消息有可能由于业务问题会一直重复发送,所以为了避免这种情况可以记录一下发送次数,当达到次数限制之后报警,人工接入处理;库存服务需要保证幂等,避免同一条消息被多次消费造成数据不一致。
本地消息表这种方案实现了最终一致性,需要在业务系统里增加消息表,业务逻辑中多一次插入的DB操作,所以性能会有损耗,而且最终一致性的间隔主要有定时任务的间隔时间决定
7.5 MQ消息事务了解吗?消息事务的原理是将两个事务通过消息中间件进行异步解耦。
订单服务执行自己的本地事务,并发送MQ消息,库存服务接收消息,执行自己的本地事务,乍一看,好像跟本地消息表的实现方案类似,只是省去 了对本地消息表的操作和轮询发送MQ的操作,但实际上两种方案的实现是不一样的。
消息事务一定要保证业务操作与消息发送的一致性,如果业务操作成功,这条消息也一定投递成功。
MQ消息事务
执行流程:
消息事务依赖于消息中间件的事务消息,例如我们熟悉的RocketMQ就支持事务消息(半消息),也就是只有收到发送方确定才会正常投递的消息。
这种方案也是实现了最终一致性,对比本地消息表实现方案,不需要再建消息表,对性能的损耗和业务的入侵更小。
7.6 最大努力通知了解吗?最大努力通知相比实现会简单一些,适用于一些对最终一致性实时性要求没那么高的业务,比如支付通知,短信通知。
以支付通知为例,业务系统调用支付平台进行支付,支付平台进行支付,进行操作支付之后支付平台会去同步通知业务系统支付操作是否成功,如果不成功,会一直异步重试,但是会有一个最大通知次数,如果超过这个次数后还是通知失败,就不再通知,业务系统自行调用支付平台提供一个查询接口,供业务系统进行查询支付操作是否成功。
最大努力通知
执行流程:
我们用比较常用的是Seata——自己去实现分布式事务调度还是比较麻烦的。
Seata 的设计目标是对业务无侵入,因此它是从业务无侵入的两阶段提交(全局事务)着手,在传统的两阶段上进行改进,他把一个分布式事务理解成一个包含了若干分支事务的全局事务。而全局事务的职责是协调它管理的分支事务达成一致性,要么一起成功提交,要么一起失败回滚。也就是一荣俱荣一损俱损~
全局事务和分支事务
Seata 中存在这么几种重要角色:
Seata工作流程
S'eata整体执行流程:
Paxos 有点类似前面说的 2PC,3PC,但比这两种算法更加完善。在很多多大厂都得到了工程实践,比如阿里的 OceanBase 的 分布式数据库, Google 的 chubby 分布式锁 。
Paxos算法是什么?Paxos 算法是 基于消息传递 且具有 高效容错特性 的一致性算法,目前公认的解决 分布式一致性问题 最有效的算法之一。
Paxos算法的工作流程?角色在Paxos中有这么几个角色:
在实际中,一个节点可以同时充当不同角色。
Paxos的三种角色
提议者提出提案,提案=编号 value,可以表示为[M,V],每个提案都有唯一编号,而且编号的大小是趋势递增的。
算法流程Paxos算法包含两个阶段,第一阶段**Prepare(准备)、第二阶段Accept(接受)**。
Paxos算法流程
Prepare(准备)阶段总结一下,接受者在收到提案后,会给与提议者两个承诺与一个应答:
当提议者收到了多数接受者的接受应答后,协商结束,共识决议形成,将形成的决议发送给所有学习节点进行学习。
所以Paxos算法的整体详细流程如下:
Paxos详细流程
算法的流程模拟,可以查看参考[13]。
Paxos算法有什么缺点吗?怎么优化?前面描述的可以称之为Basic Paxos 算法,在单提议者的前提下是没有问题的,但是假如有多个提议者互不相让,那么就可能导致整个提议的过程进入了死循环。
Lamport 提出了 Multi Paxos 的算法思想。
Multi Paxos算法思想,简单说就是在多个提议者的情况下,选出一个Leader(领导者),由领导者作为唯一的提议者,这样就可以解决提议者冲突的问题。
10.说说Raft算法?Raft算法是什么?Raft 也是一个 一致性算法,和 Paxos 目标相同。但它还有另一个名字 - 易于理解的一致性算法。Paxos 和 Raft 都是为了实现 一致性 产生的。这个过程如同选举一样,参选者 需要说服 大多数选民 (Server) 投票给他,一旦选定后就跟随其操作。Paxos 和 Raft 的区别在于选举的 具体过程 不同。
Raft算法的工作流程?Raft算法的角色Raft 协议将 Server 进程分为三种角色:
就像一个民主社会,领导者由跟随者投票选出。刚开始没有 领导者,所有集群中的 参与者 都是 跟随者。
那么首先开启一轮大选。在大选期间 所有跟随者 都能参与竞选,这时所有跟随者的角色就变成了 候选人,民主投票选出领袖后就开始了这届领袖的任期,然后选举结束,所有除 领导者 的 候选人 又变回 跟随者 服从领导者领导。
这里提到一个概念 「任期」,用术语 Term 表达。
三类角色的变迁图如下:
Raft三种角色变迁图
Leader选举过程Raft 使用心跳(heartbeat)触发Leader选举。当Server启动时,初始化为Follower。Leader向所有Followers周期性发送heartbeat。如果Follower在选举超时时间内没有收到Leader的heartbeat,就会等待一段随机的时间后发起一次Leader选举。
Follower将其当前term加一然后转换为Candidate。它首先给自己投票并且给集群中的其他服务器发送 RequestVote RPC 。结果有以下三种情况:
Leader选举
选出 Leader 后,Leader 通过 定期 向所有 Follower 发送 心跳信息 维持其统治。若 Follower 一段时间未收到 Leader 的 心跳,则认为 Leader 可能已经挂了,然后再次发起 选举 过程。
分布式设计11.说说什么是幂等性?什么是幂等性?
幂等性是一个数学概念,用在接口上:用在接口上就可以理解为:同一个接口,多次发出同一个请求,请求的结果是一致的。
简单说,就是多次调用如一次。
什么是幂等性问题?
在系统的运行中,可能会出现这样的问题:
这些都是常见的幂等性问题。
在分布式系统里,只要下游服务有写(保存、更新)的操作,都有可能会产生幂等性问题。
PS:幂等和防重有些不同,防重强调的防止数据重复,幂等强调的是多次调用如一次,防重包含幂等。
怎么保证接口幂等性?接口幂等性
计数器比较简单粗暴,比如我们要限制1s能够通过的请求数,实现的思路就是从第一个请求进来开始计时,在接下来的1s内,每个请求进来请求数就 1,超过最大请求数的请求会被拒绝,等到1s结束后计数清零,重新开始计数。
这种方式有个很大的弊端:比如前10ms已经通过了最大的请求数,那么后面的990ms的请求只能拒绝,这种现象叫做“突刺现象”。
就是桶底出水的速度恒定,进水的速度可能快慢不一,但是当进水量大于出水量的时候,水会被装在桶里,不会直接被丢弃;但是桶也是有容量限制的,当桶装满水后溢出的部分还是会被丢弃的。
算法实现:可以准备一个队列来保存暂时处理不了的请求,然后通过一个线程池定期从队列中获取请求来执行。
漏桶算法
令牌桶就是生产访问令牌的一个地方,生产的速度恒定,用户访问的时候当桶中有令牌时就可以访问,否则将触发限流。
实现方案:Guava RateLimiter限流
Guava RateLimiter是一个谷歌提供的限流,其基于令牌桶算法,比较适用于单实例的系统。
令牌桶算法
这一期的分布式面试题就整理到这里了,主要是偏理论的一些问题,分布式其实是个很大的类型,比如分布式调用、分布式治理……
所以,这篇文章只是个开始,后面还会有分布式调用(RPC)、微服务相关的主题文章,敬请期待。
原作者;https://mp.weixin.qq.com/s/d84tWIjbcGKhwUptzkO2hQ
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved