ZAB协议
ZAB(Zookeeper AtomicBroadcast)协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,
当整个集群在启动时,或者当 leader 节点出现网络中断、崩溃等情况时,ZAB 协议就会进入恢复模式(FastLeaderElection机制)并选举产生新的 Leader,当 leader 服务器选举出来后,并且集群中有过半的机器和该 leader 节点完成数据同步后(同步指的是数据同步,用来保证集群中过半的机器能够和 leader 服务器的数据状态保持一致),ZAB 协议就会退出恢复模式。当集群中已经有过半的 Follower 节点完成了和 Leader 状态同步以后,那么整个集群就进入了消息广播模式。这个时候,在 Leader 节点正常工作时,启动一台新的服务器加入到集群,那这个服务器会直接进入数据恢复模式,和 leader 节点进行数据同步。同步完成后即可正常对外提供非事务请求的处理。
原子广播(ZAB)
为了保证写操作的一致性与可用性,Zookeeper专门设计了一种名为原子广播(ZAB)的支持崩溃恢复的一致性协议。基于该协议,Zookeeper实现了一种主从模式的系统架构来保持集群中各个副本之间的数据一致性。
根据ZAB协议,所有的写操作都必须通过Leader完成,Leader写入本地日志后再复制到所有的Follower节点。
一旦Leader节点无法工作,ZAB协议能够自动从Follower节点中重新选出一个合适的替代者,即新的Leader,该过程即为领导选举。该领导选举过程,是ZAB协议中最为重要和复杂的过程。(具体请看上一篇博文!)
写操作
写Leader
通过Leader进行写操作流程如下图所示
由上图可见,通过Leader进行写操作,主要分为五步:
- 客户端向Leader发起写请求
- Leader将写请求以Proposal的形式发给所有Follower并等待ACK
- Follower收到Leader的Proposal后返回ACK
- Leader得到过半数的ACK(Leader对自己默认有一个ACK)后向所有的Follower和Observer发送Commmit
- Leader将处理结果返回给客户端
这里要注意
- Leader并不需要得到Observer的ACK,即Observer无投票权
- Leader不需要得到所有Follower的ACK,只要收到过半的ACK即可,同时Leader本身对自己有一个ACK。上图中有4个Follower,只需其中两个返回ACK即可,因为(2+1) / (4+1) > 1/2
- Observer虽然无投票权,但仍须同步Leader的数据从而在处理读请求时可以返回尽可能新的数据
读操作
Leader/Follower/Observer都可直接处理读请求,从本地内存中读取数据并返回给客户端即可。
由于处理读请求不需要服务器之间的交互,Follower/Observer越多,整体可处理的读请求量越大,也即读性能越好。
一致性保证
ZAB协议保证了在Leader选举的过程中,已经被Commit的数据不会丢失,未被Commit的数据对客户端不可见。
学习了Zookeeper的FastLeaderElection机制之后,我们会知道当Leader服务器宕机之后,剩下的服务器,首先会选举zxid号最大的机器成为新的Leader,也就是数据最‘新’的那台服务器做为新的Leader。
已经被Commit的数据不会丢失
已经被Coomit的数据,旧Leader宕机后,其它服务器根据上述FastLeaderElection算法选出新的Leader。Follower会主动将自己最大的zxid发送给新Leader,B会将Follower的zxid与自身zxid间的所有被Commit过的消息同步给Follower。
ACK数量已经大于N/2但并未Commit的数据。旧Leader宕机后,其它服务器根据上述FastLeaderElection算法选出新的Leader。Follower会主动将自己最大的zxid发送给新Leader,B会将Follower的zxid与自身zxid间的所有被Commit过的消息同步给Follower。新Leader诞生之后,先判断自身未Commit的消息是否存在于大多数服务器中从而决定是否要将其Commit。此时会未被Commit但是ACK已经大于N/2的消息,依旧会被Leader执行Commit操作。
这样保证了已经被Commit的数据不会丢失。
未Commit过的消息对客户端不可见
具体做法是,诞生的新Leader,先判断自身未Commit的消息是否存在于大多数服务器中从而决定是否要将其Commit。然后新Leader可得出自身所包含的被Commit过的消息中的最小zxid(记为min_zxid)与最大zxid(记为max_zxid)。Follower发送自身Commit过的最大消息zxid(记为max_zxid)以及未被Commit过的所有消息(记为zxid_set)。B根据这些信息作出如下操作
- 如果Follower的max_zxid与Leader的max_zxid相等,说明该Follower与Leader完全同步,无须同步任何数据
- 如果Follower的max_zxid在Leader的(min_zxid,max_zxid)范围内,Leader会通过TRUNC命令通知Follower将其zxid_set中大于Follower的max_zxid(如果有)的所有消息全部删除
这样就保证了未Commit过的消息对客户端不可见。
总结
- 由于使用主从复制模式,所有的写操作都要由Leader主导完成,而读操作可通过任意节点完成,因此Zookeeper读性能远好于写性能,更适合读多写少的场景
- 虽然使用主从复制模式,同一时间只有一个Leader,但是Failover机制保证了集群不存在单点失败(SPOF)的问题
- ZAB协议保证了Failover过程中的数据一致性
- 服务器收到数据后先写本地文件再进行处理,保证了数据的持久性