一致性算法是为了解决分布式系统的CAP理论中的一致性(Consistency)问题
数据一致性协议广泛存在于分布式系统中,例如:注册中心,消息中间件(kafka),分布式数据库
可以说只要是涉及到集群的数据一致性都需要协议来保证
分布式系统的CAP理论
CAP原则又称CAP定理,指的是在分布式系统的设计中,没有一种设计可以同时满足 Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性)3个特性,这三者不可得兼
理论首先把分布式系统中的三个特性进行了如下归纳:
- 一致性(C)
在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
- 可用性(A)
在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
- 分区容忍性(P)
实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
当发生网络故障导致一部分节点故障之后,我们就会发现一致性C和可用性A是相悖的,想要数据强一致,就必须重新选举,那么在重新选举这段时间内是无法提供服务的,这就不满足可用性了
想要一直可用,那就回出现数据不一致的状态,就违背了一致性
注册中心的CAP取舍
- (zookeeper和consul满足CP,牺牲了一部分可用性,这是由于重新选举leader导致的)
- (Eureka满足了AP,也就是说可以不满足强一致性)
CAP特性的取舍
我们分析一下既然只能满足两个,那么舍弃哪一个比较好呢?
(1)满足CA舍弃P,也就是满足一致性和可用性,舍弃容错性。但是这也就意味着你的系统不是分布式的了,因为涉及分布式的想法就是把功能分开,部署到不同的机器上
(2)满足CP舍弃A,也就是满足一致性和容错性,舍弃可用性。如果你的系统允许有段时间的访问失效等问题,这个是可以满足的。就好比多个人并发买票,后台网络出现故障,你买的时候系统就崩溃了
(3)满足AP舍弃C,也就是满足可用性和容错性,舍弃一致性。这也就是意味着你的系统在并发访问的时候可能会出现数据不一致的情况。
实时证明,大多数都是牺牲了一致性。像12306还有淘宝网,就好比是你买火车票,本来你看到的是还有一张票,其实在这个时刻已经被买走了,你填好了信息准备买的时候发现系统提示你没票了。这就是牺牲了一致性
1.2PC
2PC特点:有一个参与者执行失败,协调者会让所有参与者回滚
2PC 把数据复制分为两步:
表决阶段:主节点将数据发送给所有副本,每个副本都要响应提交或者回滚,如果副本投票提交,那么它会将数据放到暂存区域,等待最终提交
提交阶段:主节点收到其他副本的响应,如果副本都认为可以提交,那么就发送确认提交给所有副本让它们提交更新,数据就会从暂存区域移到永久区域。只要有一个副本返回回滚就整体回滚
2PC 是典型的 CA 系统,为了保证一致性和可用性,2PC 一旦出现网络分区或者节点不可用就会被拒绝写操作,把系统变成只读的
由于 2PC 容易出现节点宕机导致一直阻塞的情况,所以在数据复制的场景中不常用,一般多用于分布式事务中(注:实际应用过程中会有很多优化,例如3PC)
分区容忍性算法
我们熟知的 Paxos、ZAB、Raft 等都属于分区容忍的一致性协议,其核心思想是:一致性的保证不一定要求所有的节点都保持一致,只要大多数节点更新了(N/2+1),对于整个分布式系统来说数据也是一致性的
Paxos、Raft、ZAB都认为最终会有一个领导者,所有稳定的服务器都会信任,而一个领导者负责一个周期(Term)。如果怀疑现任领导人有问题,新领导人将提出一个新任期,必须大于前一任期
Paxos、Raft的关键区别在于如何选出领导者并保持安全。在Raft中,只有最新的服务器才能成为领导者,而Paxos允许任何服务器成为领导者。然而,这种灵活性伴随着额外的复杂性。
请注意,Raft和Paxos中的领导者可能会成为瓶颈,因为所有流量都会通过它
2.Paxos算法
paxos有 三种角色:Proposer,Acceptor,Learners
Proposer:
只要 Proposer 发的提案被半数以上 Acceptor 接受,Proposer 就认为该提案里的 value 被选定了
Acceptor:
只要 Acceptor 接受了某个提案,Acceptor 就认为该提案里的 value 被选定了
Learner:
Acceptor 告诉 Learner 哪个 value 被选定,Learner 就认为那个 value 被选定了
Paxos 算法的leader选举分为两个阶段。具体如下:
阶段一(准 leader 确定 ):
(a) Proposer 选择一个提案编号 N,然后向半数以上的 Acceptor 发送编号为 N 的 Prepare 请求
(b) 如果一个 Acceptor 收到一个编号为 N 的 Prepare 请求,且 N 大于该 Acceptor 已经响应过的所有 Prepare 请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响应反馈给 Proposer,同时该 Acceptor 承诺不再接受任何编号小于 N 的提案
阶段二(leader 确认):
(a) 如果 Proposer 收到半数以上 Acceptor 对其发出的编号为 N 的 Prepare 请求的响应,那么它就会发送一个针对[N,V]提案的 Accept 请求给半数以上的 Acceptor。注意:V 就是收到的响应中编号最大的提案的 value,如果响应中不包含任何提案,那么 V 就由 Proposer 自己决定
(b) 如果 Acceptor 收到一个针对编号为 N 的提案的 Accept 请求,只要该 Acceptor 没有对编号大于 N 的 Prepare 请求做出过响应,它就接受该提案
2.raft选举算法
nacos是一种什么样的机制来实现的集群呢?
nacos的集群类似于zookeeper, 它分为leader角色和follower角色, 那么从这个角色的名字可以看出来,这个集群存在选举的机制。 因为如果自己不具备选举功能,角色的命名可能就是master/slave了,
当然这只是我基于这么多组件的命名的一个猜测
Nacos集群采用raft算法来实现,相对于zookeeper的选举算法Raft是较为简单的一种。
在Raft中,节点有三种角色:
Leader:负责接收客户端的请求
Candidate:候选者 选举Leader的中间状态
Follower:负责响应来自Leader或者Candidate的请求
演示地址:
http://thesecretlivesofdata.com/raft/
Term(任期)
在 Raft 中使用了一个可以理解为周期(第几届、任期)的概念,用 Term 作为一个周期,每个 Term 都是一个连续递增的编号,每一轮选举都是一个 Term 周期,在一个 Term 中只能产生一个 Leader;当某节点收到的请求中 Term 比当前 Term 小时则拒绝该请求
选举lader的流程:
假如目前集群没有leader,或者leader节点挂了,当follower节点在自己的随机时间段内,没有收到leader发过来的维持心跳连接的数据,就会开启选举流程
其会马上给其他的节点发送选举的请求。收到请求的节点会响应,一旦响应了此节点的请求,就算了投了一次票了,每个节点只能投一次票,当某个节点收到了集群过半数的节点的投票(包括自己)就会自动成为leader节点
如果出现有两个节点同时开启投票,并且收到的投票数一样的情况,集群会自动开启新的投票,任然是每个节点随机一段时间开启投票,这样就可以避免每次都出现平票的局面
当一个节点成为leader节点之后,其马上就会向其他节点发送心跳包来维持心跳连接
数据更新流程:
集群中出现leader之后,leader节点负责数据的变更,具体的变更流程如下:
当leader节点收到客户端请求之后,会把数据记录到日志里面,然后会发送一个请求给所有的follower节点,follower节点接收到请求之后同样会把数据记录在日志里面,然后给leader节点响应,leader节点接收到响应之后会更改数据,然后把更改之后的数据发送给follower节点
Zab协议
ZAB( ZooKeeper Atomic Broadcast , ZooKeeper 原子消息广播协议)协议包括两种基本的模
式:崩溃恢复和消息广播
崩溃恢复:
Leader 选举:Leader 选举过程
数据同步:Leader 服务器与其他服务器进行数据同步
消息广播:
Leader 服务器将数据发送给其他服务器
1. 当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断崩溃退出与重启等异常情况时,ZAB 就会进入恢复模式并选举产生新的 Leader 服务器。
2. 当选举产生了新的 Leader 服务器,同时集群中已经有过半的机器与该 Leader 服务器完成了状态同步之后,ZAB 协议就会退出崩溃恢复模式,进入消息广播模式。
3. 当有新的服务器加入到集群中去,如果此时集群中已经存在一个 Leader 服务器在负责进行消息广播,那么新加入的服务器会自动进入数据恢复模式,找到 Leader 服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。