Distributed System - 一致性算法(Paxos, Raft, ZAB)
CAP Theorem
对于一个分布式系统,不能同时满足以下三点。
Consistency
分布式系统,每一个节点的数据都是一致的。
在分布式系统完成某写操作后任何读操作,都应该获取到该写操作写入的那个最新的值。相当于要求分布式系统中的各节点时时刻刻保持数据的一致性。
Availability
分布式提供的服务是经常可以访问的。
一直可以正常的做读写操作。简单而言就是客户端一直可以正常访问并得到系统的正常响应。用户角度来看就是不会出现系统操作失败或者访问超时等问题。
Partition Tolerance
指的分布式系统中的某个节点或者网络分区出现了故障的时候,整个系统仍然能对外提供满足一致性和可用性的服务。也就是说部分故障不影响整体使用。
事实上我们在设计分布式系统是都会考虑到bug,硬件,网络等各种原因造成的故障,所以即使部分节点或者网络出现故障,我们要求整个系统还是要继续使用的
(不继续使用,相当于只有一个分区,那么也就没有后续的一致性和可用性了)
分区:
- 中国的节点和美国的节点断开了联系(因为
- 但是每个节点都能提供对应的服务
CAP取舍
(1) CA: 优先保证一致性和可用性,放弃分区容错。 这也意味着放弃系统的扩展性,系统不再是分布式的,有违设计的初衷。
CA: RDBMS
(2) CP: 优先保证一致性和分区容错性,放弃可用性。在数据一致性要求比较高的场合(譬如:zookeeper,Hbase) 是比较常见的做法,一旦发生网络故障或者消息丢失,就会牺牲用户体验,等恢复之后用户才逐渐能访问。
CP: MongoDB,HBASE,Redis
(3) AP: 优先保证可用性和分区容错性,放弃一致性。NoSQL中的Cassandra 就是这种架构。跟CP一样,放弃一致性不是说一致性就不保证了,而是逐渐的变得一致。
CouchDB
Cassandra
DynamoDB
Riak
一致性模型
弱一致性
最终一致性:最终达到一致性(不能立即得到正确的回复,在后面的某个时间点回复正确答案)
- DNS(Domain Name System)
- Gossip(Cassandra 的通信协议)
强一致性
- 同步
- Paxos
- Raft(multi-paxios)
- ZAB(multi-paxos)
明确问题
鸡蛋不能放在同一个篮子里、
数据不能单点存储
分布式系统对于Fault Tolerance的解决方案一般是:State Machine Replication(consensus)算法。(log replication)
paxos其实是一个共识算法。系统的最终一致性。不仅需要达成共识,还会取决于Client的行为
强一致性算法
主从同步
- Master接受写请求
- Master复制日志到Slave
- Master等待,直到所有从库返回
存在的问题:
- 一个节点失败,Master阻塞,导致整个集群不可用
- 保证了一致性,可用性大大降低
多数派
基本思想:
- 每次写都保证写入的节点大于n/2节点,每次读从大于n/2节点中读
存在的问题:
- 并发环境下,无法保证系统正确性,顺序非常重要
Paxos
Lesile Lamport, Latex的发明者
为描述Paxos算法,Lamport虚拟了一个叫做Paxos的希腊城邦,这个岛按照议会民主制的政治模式制订法律,但是没有人愿意将自己的全部时间和精力放在这种事。所以无论是议员,议长或者传递纸条的服务员都不能承诺别人需要时一定会出现,也无法承诺批准决议或者传递消息的时间。
Paxos(牌咳嗽嘶)
- Basic Paxos
- Multi Paxos
- Fast Paxos
Baisc Paxos
角色介绍
- Client:系统外部橘色,请求发起者。民众
- Propser:接受Client请求,向集群提出提议Propose。并在冲突产生式,调节冲突。像议员,替民众提出议案。
- Acceptor(Voter):提议投票和接受者,只有在形成法定人数(Quorum,一般为Majority多数派)时,提议才会被最终接受,像国会。
- Learner:提议接受者,backup,备份,对集群一致性没什么影响。像记录员。
Phase 1a : Prepare
proposer 提出提案,编号为N,此N大于这个proposer之前提出的提案编号。请求Acceptor的quorum接受。
Phase 1b : Promise
如果N大于此acceptor之前接受的任何提案编号则接受,否则拒绝
Phase 2a: Accept
如果达到了多数派,Proposer会发出Accept请求,此请求包含提案编号和内容。
Phase 2b: Accepted
如果此acceptor在此期间没有收到任何编号大于N的提案,则接受此提案内容,否则忽略。
存在的问题:
活锁、难实现、效率低(2轮RPC)
Multi Paxos
引入Leader:唯一的Proposer,所有的请求都需要经过该Leader
Raft
达到一致需要共识和客户端的行为
保证共识,划分为三个子问题:
Leader Election:怎么选
Log Replication:log 同步到其它结点
Safety:共识过程中每一存在错误
重新定义角色
Leader
Follower:Follow Leader的决策
Candidate:Follower状态
Log Replication过程
-
初始状态:都是follower状态
-
如果followers长时间没有收到leader的消息,他们就可以成为candidate
-
其中一个节点会首先发起 竞选Leade投票请求:其它Follower 回复该投票
-
拿到投票之后就可以成为Leader
-
成为Leader之后 所有请求都要经过Leader
-
一个请求到来
set 5
-
Leader先写入本地log,再向Followers发送写请求
set 5
-
Followers收到请求后会告诉leader写入完毕,Leader提交写入操作 此时数据真的被修改为5,通知Follower 可以提交了
-
最后整个系统的数据达到一致
Leader Election
- 一个Timer Election Timeout
- 在一个Timer中,没有收到Leader的消息, 某个Follower首先结束了Timer,开始竞选Leader,成为Candidate 并且new一个任期(一个递增的ID 第一任总统),记录Vote Count(自身一票 + 其它Follower Vote的票),向其它Follower请求投票给自己。如果拥有了大多数的投票就称为Leader。收到投票请求的Follower就会刷新Timer
- 成为Leader之后,对于一个Heart Beat timeout,发送一个心跳包(可以夹杂数据),刷新其它Follower Timer,维护自身的权利。
- 任期持续到某个Follower的Timer没有收到心跳包
- 该Follower成为Candidate重复之前的步骤。
Split Vote解决
两个节点同时成为了 Candidate,竞选同一任期总统。
解决方法:参与竞选的节点,各自等待一个随机的时间。
Partition解决
解决分区问题后:根据任期期间来判断那一部分是新的,分区恢复。
ZAB
基本与raft相同
在一些名词的叫法上有不同:如AZB将某一个Leader的周期称为epoch,而raft则称为term
实现上的不同:raft保证日志连续性,心跳方向为leader至follower。ZAB则相反。
项目实践
Zookeeper(ZAB)集群搭建和命令行操作
ZK采用配置文件的方法启动