CAP
常见CAP场景,只能满足其中两者
CAP 的意思就是说我们最多只能保证其中两个条件同时成立。其实大部分情况下,我们都是在一致性和可用性之间取舍而已。
-
假如要满足一致性,
那么,我们只能让请求另一个节点的操作暂时 hang 住,返回 client 失败或者超时的结果,
这种情况多发生在银行柜台等对数据一致性要求很高的情境下,因为比起保证用户资金数目的正确性比暂时让用户无法操作要更重要一些
-
假如要满足可用性,
因为网络已经隔离,也就没办法达到一致性,
这种情况多发生在互联网行业中,比如新闻等对数据一致性要求不高但对可用性要求高的情况下,毕竟,用户压根看不了新闻比看不到及时新闻要重要的多。
Consistency:一致性,Consensus:协同
Consistency 几乎被业界用烂,以至于当我们在讨论一致性的时候,其实我们都无法确定对方所说的一致性是不是和自己的那个一致。
我们常说的一致性(Consistency)在分布式系统中指的是对于同一个数据的多个副本,
其对外表现的数据一致性,如线性一致性、因果一致性、最终一致性等,都是用来描述副本问题中的一致性的。
而共识(Consensus)则不同,简单来说,共识问题是要经过某种算法使多个节点达成相同状态的一个过程。
在我看来,一致性强调结果,共识强调过程。
共识有个更高逼格的称呼:基于状态机复制的共识算法
共识模型
1.主从同步
主从同步模型存在致命问题:只要一个节点失败,则 Master 就会阻塞,导致整个集群不可用,保证了一致性,可用性缺大大降低了。
2.多数派
每次写入大于 N/2 个节点,每次读也保证从 N/2 个节点中读。
多数派的模型看似完美解决了多节点的一致性问题,可是在并发环境下,因为每个节点的操作日志写入顺序无法保证一致,也就无法保证最终的一致性。
Paxos
分布式系统要做到 fault tolorence,就需要共识模型。
而节点达成共识,不仅需要节点之间的算法,还会取决于 client 的行为:比如即使副本系统使用 multi-paxos 在所有副本服务器上同步了日志序号,但如果 Client 被允许从非 Leader 节点写入数据,则整个副本系统仍然不是强一致的。
角色介绍:
Client: 系统外部角色,请求发起者。如民众。
Proposer: 接受 Client 请求,向集群提出提议(propose)。并在冲突发生时,起到冲突调节的作用。如议员,替民众提出议案。
Accpetor: 提议投票和接收者,只有在形成法定人数(Quorum,即 Majority 多数派)时,提议才会最终被接受。如国会。
Learner: 提议接受者,backup,备份,对集群的一致性没影响。如记录员。
1.Phase1a: Prepare
proposer 提出一个议案,编号为 N,N 一定大于这个 proposer 之前提出的提案编号,请求 acceptor 的 quorum(大多数) 接受。
2.Phase1b: Promise
如果 N 大于此 acceptor 之前接受的任何提案编号则接受,否则拒绝。
3.Phase2a: Accept
如果达到了多数派,proposer 会发出 accept 请求,此请求包含上一步的提案编号和提案内容。
4.Phase2b: Accepted
如果此 acceptor 在此期间没有收到任何大于 N 的提案,则接受此提案内容,否则忽略。
Paxos 存在活锁问题
当 第一个 proposer 在第一阶段发出请求,还没来得及后续的第二阶段请求,
紧接着第二个 proposer 在第一阶段也发出请求,如果这样无穷无尽,acceptor 始终停留在决定顺序号的过程上,那大家谁也成功不了.
遇到这样的问题,其实很好解决,如果发现顺序号被新的 proposer 更新了,则引入一个随机的等待的超时时间,这样就避免了多个 proposer 发生冲突的问题。
Multi Paxos
由于 Paxos 存在的问题:难实现、效率低(2 轮 rpc)、活锁。
因此又引入了 Multi Paxos,Multi Paxos 引入 Leader,也就是唯一的 proposer,所有的请求都需经过此 Leader。
因为只有一个节点维护提案编号,这样,就省略了第一轮讨论提议编号的过程。
然后进一步简化角色。
Servers 中第左边的就是 Proposer,另外两个和自身充当 acceptor,这样就更像我们真实的系统了。Raft 和 ZAB 协议其实基本上和这个一致,两者的差别很小,就是心跳的方向不一样。
在一个fault torlerance 的分布式架构下,如何尽量保证其整个系统的可用性和一致性?
最理想的模型当然是 Paxos,然而理论到落地还是有差距的,所以诞生了 Raft 和 ZAB,虽然只有一个 leader,但我们允许 leader 挂掉可以重新选举 leader,这样,中心式和分布式达到了一个妥协。
Raft
在Raft中有存在唯一的leader,由leader全权负责响应用户的请求,leader对用户请求的操作确定顺序并传递给其他follower,并在可以提交操作时通知其他follower。
如果leader发生了故障,则执行leader election过程选出新的leader。
具体流程如下:
- 该副本增加自己的current term并转换为candidate状态,它将为自己投票并向其他副本发出请求投票的消息。
- 如果candidate收到来自于更新term(current term大于或等于自己的值)的leader发来的消息,则认同该leader,转换为follower继续运行。
- 当副本接收到来自其他副本请求投票的消息时,如果该投票请求的current term大于自己的term,则首先更新自己的current term,然后它将对比本地log与发出请求投票消息的candidate的log哪个比较新(比较log最后条目的index和term,见下文),如果本地的比较新,则拒绝为该candidate投票,反之对candidate投票。 另外,每个副本在每个term中仅能投出一票。
- 当candidate收集到超过总数一半的投票时,它将变为新的leader并作为leader开始工作。
split vote
多个follower几乎同时转换为candidate并发起投票,结果最后没有任何一个candidate获得了足够的投票。
当出现这种情况时,candidate会在等待超时后进入下一个term重新开始leader election。
为了避免这种的情形重复发生,Raft中每个副本随机选择超时时间(论文中例子为在150-300ms中),降低了冲突发生的可能性。
log replication
leader接受到client的请求后,会为该操作生成一条log项,并同时记录该项的index(表明该项在log中的位置)
然后,leader会向所有follower发送该请求,将该log项传播出去,如果有副本失败的情况,leader会不断执行重传。
当leader成功将该log项复制到超过一半的副本上后,leader认为该log项(及其之前的所有log项)可以被提交了(仅限于当前term的log项,见下文),它将在本地状态机执行对应操作,并向client返回执行结果,
leader记录已提交的最高index,并告知follower,follower据此知晓已确认提交的操作并在本地执行。
然而在考虑到各类故障的影响,各个副本上的log可能会出现各种不一致的情况:
Raft用来处理这种情况的对策很简单:以leader上的日志为准,将与leader不一致的日志进行重写(这个过程比较繁琐,但思路是简单的,通过不断向前检查follower上log项,直到找到分叉点,然后进行修正)。
这样的重写使得旧term中遗留的log项可能出现被覆盖丢失的情况
总结讨论
集中式与非集中式
观察Paxos和前文中的VR和Raft算法,最大的区别在于Paxos是“非集中式”的,
在Paxos中不存在地位特殊的进程,引入选主也只是因为多个活动的proposers可能导致活锁;
VR和Raft是基于“集中式”的设计的,它们在算法中自带了选主,并要求在每个“view”或“term”中只能存在一个leader,由leader来负责定序等操作,这样极大的简化了设计和实现的难度,
在系统设计中,简单不一定是个坏事,即使是使用了Paxos算法的Chubby系统,实际上也只是使用Paxos来完成选主,当选主结束后Chubby仍然是作为一个“集中式”的系统来运行的。
多副本读一致性操作
强一点的一致性要求
Hard:
针对读请求这类不影响状态机状态的操作,可以不将其作为需要在状态机副本中同步的操作,而直接由leader副本处理,这样产生的一个问题是可能会由于网络分区等异常而读到失效的错误信息,违背了副本的线性化能力(强一致性)
Leases:
只要当leader持有Lease,就可以直接响应读请求,而其他副本也保证在Lease失效前不会产生新的leader,因此保证了一致性要求。
更加放松的一致性要求:
副本可读:
则可以允许非leader节点响应读请求,达到负载均衡的效果,通过对client的请求引入向量时钟(vector timestamps)或Lamport时钟,可以在这样的模式中实现较弱的一致性要求。
缓存机制:
例如Chubby和Zookeeper都在client端为数据做了缓存,在Chubby中client只允许访问leader进程,但对数据使用了基于Leases的缓存机制,保证了副本的线性化能力
而Zookeeper则放松了一致性要求,client不仅可以访问非leader的副本,而且使用“watch”机制提高系统处理能力。
参考资料:
thesecretlivesofdata.com/raft/