cap原则,2000年由 Eric Brewer 提出
CAP原则核心
Consistency:一致性【在集群中任何一个节点获取数据都是相同的】
强调数据一致性
Avaliability:可用性【在有效时间内返回结果】
强调一定时间内
Partition Tolerance:分区容错性 【集群部署】
强调高可用性,但必须在集群环境中(没有集群哪有分区)
三种核心中只能保证同时满足两种特征
AC: 单机应用(极少存在)
CP: 集群部署,数据一致性 银行【涉及到钱的 金融相关】
AP: 可用性 和 分区容错性 (一般来说,放弃强一致性,追求分区容错性和可用性,是很多分布式系统设计的选择)
由ACP原则衍生出BASE理论
BA: 基本可用-保留基本功能不影响当前正常运作
举个栗子:
抖音曾因《内涵段子》下架而关闭评论功能;
秒杀抢购人数过多,可能出现排队提示或限流提示等
旨在要求系统能够基本运行,一直提供服务,未知故障时允许损失部分可用性。
S:软状态一致性 当写入集达到一半即达到软状态一致性
E:最终一致性 最终能达到一致性即可 (秒杀显示还有库存,生成订单时同步数据被抢空)
这里提到一致性,那么数据一致性又怎么理解呢?
定义:
一些分布式系统通过复制数据来提高系统的可靠性和容错性,并且将数据的不同的副本存放在不同的机器
在数据有多分副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内容冲突。
所以为了避免这种情况,或者在不同的应用场景中将这种情况带来影响尽量降低。有三种模型:
强一致性:
要求无论更新操作实在哪一个副本执行,之后所有的读操作都要能获得最新的数据。
弱一致性:
用户读到某一操作对系统特定数据的更新需要一段时间,我们称这段时间为“不一致性窗口”。
最终一致性:
是弱一致性的一种特例,保证用户最终能够读取到某操作对系统特定数据的更新。
从客户端来看,有可能暂时获取的不是最新的数据,但是最终还是能访问到最新的
从服务端来看,数据存储并复制到分布到整个系统超过半数的节点,以保证数据最终一致
简单了解Paxos算法
Paxos 描述了这样一个场景:
- 有一个叫做 Paxos 的小岛(Island)上面住了一批居民(Islander);
- 岛上面所有的事情由一些特殊的人决定,他们叫做议员(Senator);
- 议员的总数(Senator Count)是确定的,不能更改;
- 岛上每次环境事务的变更都需要通过一个提议(Proposal),每个提议都有一个编号(PID),这个编号是一直增长的,不能倒退;
- 每个提议都需要超过半数((Senator Count)/2 +1)的议员同意才能生效(少数服从多数);
- 每个议员只会同意大于当前编号的提议,包括已生效的和未生效的;
- 如果议员收到小于等于当前编号的提议,他会拒绝,并告知对方:你的提议已经有人提过了。这里的当前编号是每个议
员在自己记事本上记录的编号,他会不断更新这个编号;
- 整个议会不能保证所有议员记事本上的编号总是相同的;
- 现在议会有一个目标:保证所有的议员对于提议都能达成一致的看法。
现在议会开始运作,所有议员一开始记事本上面记录的编号都是0。有一个议员发了一个提议:将电费设定为1元/度。他首先看了一下记事本,嗯,当前提议编号是0,那么我的这个提议的编号就是1,于是他给所有议员发消息:1号提议,设定电费1元/度。其他议员收到消息以后查了一下记事本,哦,当前提议编号是0,这个提议可接受,于是他记录下这个提议并回复:我接受你的1号提议,同时他在记事本上记录:当前提议编号为1。发起提议的议员收到了超过半数的回复,立即给所有人发通知:1号提议生效!收到的议员会修改他的记事本,将1好提议由记录改成正式的法令,当有人问他电费为多少时,他会查看法令并告诉对方:1元/度。
再看冲突的解决:假设总共有三个议员S1-S3,S1和S2同时发起了一个提议:1号提议,设定电费。S1想设为1元/度, S2想设为2元/度。结果S3先收到了S1的提议,于是他做了和前面同样的操作。紧接着他又收到了S2的提议,结果他一查记事本,咦,这个提议的编号小于等于我的当前编号1,于是他拒绝了这个提议:对不起,这个提议先前提过了。于是S2的提议被拒绝,S1正式发布了提议: 1号提议生效。S2向S1或者S3打听并更新了1号法令的内容,然后他可以选择继续发起2号提议。
活锁的产生是由于偶数个议员,33对立造成的。
解决方案:
1、使用奇数台服务器,但是奇数台有一台宕机依然为偶数台也是不行的。(而且多台服务能同时接收请求并同时更新数据会产生冲突,无法满足最终数据一致性,是不合理的)
2、使用有主模型,所有的提议都只能有总统来发出。
总统选举过程:
多各节点依次启动时,第一台会给自己投票,等第二台启动时因为其PID都是0,所以比较MID,后启动的MID大所以第二台会得到自己和第一台的相加的两票,以此类推,当得票数>(n/2)时的那个节点就是总统。
总统挂掉以后怎么办?快速选择总统?
找PID最接近公共PID的议员,但是不能大于公共PID【因为不是法令】。
如果有多个议员PID相同,那么就选择MYID最大的那个【议员3、议员4=>议员4】
如果网络波动就会出现脑裂出现多个主节点,最终合并选主必须过半原则。
两个主需要看那边议员多谁就做最新的总统。
节点越多业务能力越强,但是选主越慢!
Raft算法
Raft 算法的官网:https://raft.github.io/
中文版:https://acehi.github.io/thesecretlivesofdata-cn/raft/
英文版:http://thesecretlivesofdata.com/raft/
Raft有一个明确的场景,就是管理复制日志的一致性。
选择leader作为主节点
选取时将Server分为三种角色
选主过程:
分别是最开始都为
追随者
Follower:
选择过程中拥有一个随机生成的150-300ms的计时,率先走完时间的Server成为Candidate
”候选人“
Candidate:
”候选人“会在成为 Candidate时拉票,向其他服务器发送请求,同时给自己投票且自身Term(任期)增加1
,其他Server收到请求时会立即重新生成新的随机时间并重新开始计时,同时返回请求为“候选者”投票,票数高者并且>(n/2+1)就成为Leader。
最终成为主服务器(负责Client交互和log复制,同一时刻系统中最多存在1个)
Leader
当Leader收到大多数(n/2+1)Follower的ACK信息后将该日志设置为已提交并追加到本地磁盘中
日志复制(Log Replication)
主要用于保证节点的一致性,这阶段所做的操作也是为了保证一致性与高可用性。
当Leader选举出来后便开始负责客户端的请求,所有事务(更新操作)请求都必须先经过Leader处理
日志复制(Log Replication)就是为了保证执行相同的操作序列所做的工作。
在Raft中当接收到客户端的日志(事务请求)后先把该日志追加到本地的Log中
然后通过heartbeat把该Entry同步给其他Follower,Follower接收到日志后记录日志然后向Leader发送ACK
当Leader收到大多数(n/2+1)Follower的ACK信息后将该日志设置为已提交并追加到本地磁盘中
通知客户端并在下个heartbeat中Leader将通知所有的Follower将该日志存储在自己的本地磁盘中。
ZAB协议
Zoopeeker原子广播 借鉴Paxos算法。
Zoopkeeper是通过ZAB协议来保证分布式事务最终的一致性的。
实现了主备模式的系统框架,用来保持集群中各个副本之间的数据一致性
原理
发现:Zoopeeker集群必须选出一个Leader进程,同时Leader会维护一个follower可用列表
同步:实现多副本储存,Leader要负责将本身的数据与follower同步
广播:leader可以接受客户端新的proposal请求,将新的proposal请求广播给所有的follower。
Zoopeeker集群中的角色分配:
总统:
集群中所有修改数据的指令必须由总统发出
总统是由议员投票产生的(无主-->有主)
选举条件:首先按照事务zxid进行排序,如果事务相同按照myid排序
议员:
接受客户端请求
查询直接返回结果(有可能数据不一致)
写入数据,先将数据写入到当前server
发送消息给总统,总统将修改数据的命令发送给其他server
其他server接受命令后开始修改数据,修改完成后给总统返回成功的消息
当总统发现超过半数的人都修改成功,就认为修改成功了
并将信息传递给接受请求的zkServer,zkServer将消息返回给客户端,说明数据更新完成
分为Follower和Observer
Follower
拥有选举权,拥有投票权
接受客户端的访问
如果客户端执行写请求,只是将请求转发给Leader
Observer
只可以为客户端提供数据的查询和访问
如果客户端执行写请求,只是将请求转发给Leader