0.两阶段提交
在学习zab协议之前,我们先来学习一下二阶段提交的过程。
两阶段提交顾名思义主要分为两个阶段
第一阶段(请求阶段)
协调者
首先会发送某个事务的执行请求给其它所有的参与者,当参与者收到perpare请求时会检查自身并告诉协调者
自己的决策是同意还是取消
第二阶段(提交阶段)
协调者
将根据第一阶段的投票结果发送提交或回滚请求(一般是所有参与者都返回同意就发送提交请求,否则发送回滚请求)。
PS :请参考下 paxos 算法
下面回到我们的zab协议。
1.zab 是什么?
zab 的英文全称是: ZooKeeper Atomic Broadcast
zab 协议是为分布式协调服务的 zookeeper 提供崩溃恢复和 主从同步数据(维护数据一致性)的一种协议。
zab协议参考了 paxos 协议的一些设计思想。比如以下的一些特性:
- leader 向 follows 提出提案(proposal)
- leader 需要在达到法定数量(半数以上)的 follows 确认之后才会进行commit
- 每一个proposal都有一个纪元(epoch)号,类似于Paxos中的选票(ballot)
ps: 关键术语真的很重要。
zab和Paxos最大的不同是,ZAB主要是为分布式主备系统设计的,而Paxos的实现是一致性状态机(state machine replication)
1.1 节点的三种状态
zk集群有三种角色:
- leader 就是我们说的主服务器
- follower 就是我们说的从服务器
- observer 可以认为是主的clone copy,不参与投票
zk集群的一个节点,有三种状态:
- looking 选举状态,当前群龙无首;
- leading leader才有的状态;
- following follower才有的状态
一个集群中,有且只有一个 leader,只有leader 才是术语leading 状态。就如同一个国家只有一个皇帝,却可以有多个领主。
1.2 zxid
我们暂且把他当做是一个znode 的事务ID,如同mysql 每个事务,都有一个唯一的编号。
zxid 全局唯一,且是自增。
ZooKeeper会为每一个事务生成一个唯一且递增长度为64位的ZXID,
ZXID由两部分组成:低32位表示计数器(counter)和高32位的纪元号(epoch)。epoch为当前leader在成为leader的时候生成的,且保证会比前一个leader的epoch大
我们可以抽象映射为现实的场景,比如一个国家有n多的领土(国王的儿子),但是只有一个国王。古时候消息比较阻塞,选举的国王发布的协议,通过 第20任国王的第3条协议 这样记录。
epoch 为 第20任国王,counter 为第三条协议。
下一任国王在选举的时候,自动投票为第21人国王选举人。
实际上当新的leader选举成功后,会拿到当前集群中最大的一个ZXID,并去除这个ZXID的epoch,并将此epoch进行加1操作,作为自己的epoch。
1.3 历史队列
每一个follower节点都会有一个先进先出(FIFO)的队列用来存放收到的事务请求,保证执行事务的顺序
可靠提交由ZAB的事务一致性协议保证 全局有序由TCP协议保证 因果有序由follower的历史队列(history queue)保证
2.Zab的工作模式:消息广播
zab 是如何保持数据一致性的呢?
2.1 读操作
zk集群中的多个服务器,都可以提供读服务。客户端连接到集群中的任一个server节点,可以进行正常的读操作。
2.2 写操作
广播模式:
1.客户端发出写入数据请求给任意Follower。
2.Follower把写入数据请求转发给Leader。
3.Leader采用二阶段提交方式,先发送Propose广播给Follower。
此时已经生成这个事务对一个全局唯一ZXID
4.Follower接到Propose消息,写入日志成功后,返回ACK消息给Leader。 :step1
写入日志到本地磁盘
follower节点将收到的事务请求加入到历史队列(history queue)
5.Leader接到半数以上ACK消息,返回成功给客户端,并且广播Commit请求给Follower。 :step2
当follower收到commit请求时,会判断该事务的ZXID是不是比历史队列中的任何事务的ZXID都小,如果是则提交,如果不是则等待比它更小的事务的commit
zab 只能通过leader来处理写请求。集群中follow节点接受到写请求后,会发送消息到leader节点。
Zab协议要保证同一个Leader发起的事务要按顺序被执行,同时还要保证只有先前Leader的事务被执行之后,新选举出来的Leader才能再次发起事务Zab协议既不是强一致性,也不是弱一致性,而是处于两者之间的单调一致性。它依靠事务ID和版本号,保证了数据的更新和读取是有序的
3、崩溃恢复
当整个服务框架启动过程中或Leader服务器出现网络中断、崩溃退出与重启等异常情况时,Zab协议就会进入恢复模式并选举产生新的Leader服务器。
恢复模式大致可以分为四个阶段
- 选举
- 发现
- 同步
- 广播
恢复过程的步骤大致可分为
- 当leader崩溃后,集群进入选举阶段,开始选举出潜在的新leader(一般为集群中拥有最大ZXID的节点)
- 进入发现阶段,follower与潜在的新leader进行沟通,如果发现超过法定人数的follower同意,则潜在的新leader将epoch加1,进入新的纪元。新的leader产生
- 集群间进行数据同步,保证集群中各个节点的事务一致
- 集群恢复到广播模式,开始接受客户端的写请求
3.1 选举阶段
选举阶段,此时集群中的节点处于Looking状态。它们会各自向其他节点发起投票,投票当中包含自己的服务器ID和最新事务ID(ZXID)。
接下来,节点会用自身的ZXID和从其他节点接收到的ZXID做比较,如果发现别人家的ZXID比自己大,也就是数据比自己新,那么就重新发起投票,投票给目前已知最大的ZXID所属节点。
每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票。如果存在这样的节点,该节点将会成为准Leader,状态变为Leading。其他节点的状态变为Following。
选举比较总结:(投票内容:(zxid,myid))
规则是优先检查ZXID。ZXID比较大的服务器优先作为Leader。
如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器,
PS : zk是cp的,不一定保证可用性,在选举的过程中,不能对外提供服务
3.2 发现阶段
发现阶段,用于在从节点中发现最新的ZXID和事务日志。
或许有人会问:既然Leader被选为主节点,已经是集群里数据最新的了,为什么还要从节点中寻找最新事务呢?
这是为了防止某些意外情况,比如因网络原因在上一阶段产生多个Leader的情况。
所以这一阶段,Leader集思广益,接收所有Follower发来各自的最新epoch值。Leader从中选出最大的epoch,基于此值加1,生成新的epoch分发给各个Follower。
各个Follower收到全新的epoch后,返回ACK给Leader,带上各自最大的ZXID和历史事务日志。Leader选出最大的ZXID,并更新自身历史日志。
3.3 同步阶段
同步阶段,把Leader刚才收集得到的最新历史事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才能成为正式的Leader。
3.4 广播阶段
自此,故障恢复正式完成。可以正常接收客户端的读写请求。
10、参考链接
推荐,算法详解
https://dbaplus.cn/news-141-1...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。