0.两阶段提交

在学习zab协议之前,我们先来学习一下二阶段提交的过程。

image.png

两阶段提交顾名思义主要分为两个阶段

第一阶段(请求阶段)

协调者首先会发送某个事务的执行请求给其它所有的参与者,当参与者收到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集群有三种角色:

  1. leader 就是我们说的主服务器
  2. follower 就是我们说的从服务器
  3. observer 可以认为是主的clone copy,不参与投票

zk集群的一个节点,有三种状态:

  1. looking 选举状态,当前群龙无首;
  2. leading leader才有的状态;
  3. following follower才有的状态

一个集群中,有且只有一个 leader,只有leader 才是术语leading 状态。就如同一个国家只有一个皇帝,却可以有多个领主。

1.2 zxid

我们暂且把他当做是一个znode 的事务ID,如同mysql 每个事务,都有一个唯一的编号。
zxid 全局唯一,且是自增。

image.png

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 写操作

广播模式

image.png

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节点。

image.png

Zab协议要保证同一个Leader发起的事务要按顺序被执行,同时还要保证只有先前Leader的事务被执行之后,新选举出来的Leader才能再次发起事务

Zab协议既不是强一致性,也不是弱一致性,而是处于两者之间的单调一致性。它依靠事务ID和版本号,保证了数据的更新和读取是有序的

3、崩溃恢复

当整个服务框架启动过程中或Leader服务器出现网络中断、崩溃退出与重启等异常情况时,Zab协议就会进入恢复模式并选举产生新的Leader服务器。

恢复模式大致可以分为四个阶段

  • 选举
  • 发现
  • 同步
  • 广播

恢复过程的步骤大致可分为

  1. 当leader崩溃后,集群进入选举阶段,开始选举出潜在的新leader(一般为集群中拥有最大ZXID的节点)
  2. 进入发现阶段,follower与潜在的新leader进行沟通,如果发现超过法定人数的follower同意,则潜在的新leader将epoch加1,进入新的纪元。新的leader产生
  3. 集群间进行数据同步,保证集群中各个节点的事务一致
  4. 集群恢复到广播模式,开始接受客户端的写请求

3.1 选举阶段

选举阶段,此时集群中的节点处于Looking状态。它们会各自向其他节点发起投票,投票当中包含自己的服务器ID和最新事务ID(ZXID)。

image.png

接下来,节点会用自身的ZXID和从其他节点接收到的ZXID做比较,如果发现别人家的ZXID比自己大,也就是数据比自己新,那么就重新发起投票,投票给目前已知最大的ZXID所属节点。

image.png

每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票。如果存在这样的节点,该节点将会成为准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、参考链接

聊聊zookeeper的ZAB算法

漫画:什么是ZooKeeper

二阶段提交2PC,3 阶段PC

推荐,算法详解

https://dbaplus.cn/news-141-1...

https://zhuanlan.zhihu.com/p/...

https://zhuanlan.zhihu.com/p/...


天真真不知路漫漫
70 声望6 粉丝

1