ZAB协议

ZAB协议定义

ZAB协议是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议,实现分布式数据一致性。

ZAB协议组成部分

消息广播

  • 集群中所有的事务请求都由Leader节点来处理,其他服务器为Follower
  • Leader将客户端的事务请求转换为事务Proposal,并且将Proposal分发给集群中其他所有的Follower
  • 完成广播之后,Leader等待Follower反馈,当有过半数的Follower反馈信息后,Leader将再次向集群内Follower广播Commit信息
  • Commit信息就是确认将之前的Proposal提交
  • Leader节点的写入是一个两步操作:第一步是广播事务操作,第二步是广播提交操作
  • 过半数指的是反馈的节点数 >=N/2+1,N是全部的Follower节点数量

崩溃恢复

  • 在以下情况下会进入崩溃恢复模式:
    • 初始化集群,刚刚启动的时候
    • Leader崩溃,因为故障宕机
    • Leader失去了半数的机器支持,与集群中超过一半的节点断连
  • 此时开启新一轮Leader选举,选举产生的Leader会与过半的Follower进行同步,使数据一致
  • 当与过半的机器同步完成后,就退出恢复模式,然后进入消息广播模式

ZAB协议作用

  • 所有客户端请求都是写入到Leader进程中,然后由Leader同步到其他节点(Follower)
  • 在集群数据同步的过程中,如果出现Follower节点崩溃或者Leader进程崩溃时,都会通过Zab协议来保证数据一致性
  • 整个Zookeeper集群的一致性保证就是在消息广播和崩溃恢复两个状态之间切换
  • 当Leader服务正常时,就是正常的消息广播模式;当Leader不可用时,则进入崩溃恢复模式
  • 崩溃恢复阶段会进行数据同步,完成以后,重新进入消息广播阶段

Zxid事务编号

  • Zxid是Zab协议的一个事务编号
  • Zxid是一个64位的数字,其中低32位是一个简单的单调递增计数器,针对客户端每一个事务请求,计数器加1
  • 高32位则代表Leader周期年代的编号
  • Leader周期(epoch):可以理解为当前集群所处的年代或者周期
  • 每当有一个新的Leader选举出现时,就会从这个Leader服务器上取出其本地日志中最大事务的Zxid,并从中读取epoch值,然后加1,以此作为新的周期ID
  • 高32位代表了每代Leader的唯一性,低32位则代表了每代Leader中事务的唯一性

ZAB节点状态

  • following:服从leader的命令
  • leading:负责协调事务
  • election/looking:选举状态

ZAB选举流程

好的,我们来分步骤详细讲解 ZAB 协议的选举流程,并重点解释 Zxid 和 myid 的作用。

ZAB 选举流程详解

ZAB 的选举发生在集群启动或 Leader 崩溃时,所有节点会进入 LOOKING 状态。整个选举过程可以概括为“两轮比较”,核心是确保选出的 Leader 拥有最全的数据。

步骤 1: 每个节点发起投票

  • 当某个 Follower 发现无法与 Leader 通信(心跳超时)时,它会将自己置为 LOOKING 状态,并首先投自己一票
  • 这张选票包含三个核心信息:
    1. 我当前所推举的 Leader 的 myid(初始时就是自己)。
    2. 我本地持有的最大 Zxid (lastZxid)。
    3. 我当前所处的逻辑时钟 Epoch

步骤 2: 节点间交换选票

  • 每个处于 LOOKING 状态的节点,都会通过 投票广播 将自己的这张选票发送给集群中的所有其他节点。
  • 同时,它也会不断地接收来自其他节点的选票。

步骤 3: 接收选票并进行“两轮比较”

这是整个选举过程的核心。当一个节点收到另一张选票时,它会将收到的选票与自己当前所持有的选票进行比较。比较规则遵循一个严格的优先级,即“数据完整性优先,服务器ID为辅”:

  1. 第一轮比较:比较 Zxid (数据新旧)

    • 规则:优先比较双方选票中携带的 lastZxid(即所推举 Leader 的 Zxid)。
    • 如果收到的选票的 Zxid > 自己当前选票的 Zxid
      • 这证明对方推举的 Leader 拥有比自己推举的 Leader 更更新的数据
      • 为了保证集群数据的一致性,必须让拥有最全数据的人当 Leader,否则会造成数据丢失或回滚。
      • 行动:节点会无条件地更新自己的选票,改为支持对方推举的那个 myid,并将这个更新后的选票再次广播出去。
    • 如果收到的选票的 Zxid < 自己当前选票的 Zxid
      • 说明对方的数据更旧,直接忽略这张选票
  2. 第二轮比较:比较 myid (服务器ID)

    • 只有当双方选票的 Zxid 相等时,才会进入这一轮比较。
    • 规则:比较双方选票中推举的 Leader 的 myid
    • 如果收到的选票的 myid > 自己当前选票的 myid
      • 在数据一样新的前提下,选择那个 ID 更大的服务器作为 Leader。这是一个打破平局的机制,确保大家能快速达成一致,避免僵局。
      • 行动:节点会更新自己的选票,改为支持那个 myid 更大的服务器,并广播更新。

步骤 4: 统计选票与确认 Leader

  • 节点在不断地接收和更新选票的过程中,也会统计有多少张选票最终指向了同一个 myid
  • 一旦发现某个被推举的 myid 获得了超过半数(Quorum) 的投票,该节点就会确认这个 myid 对应的服务器就是新的 Leader。
  • 此时,该节点将自己的状态从 LOOKING 切换为 FOLLOWING(如果自己是 Leader 则切换为 LEADING),并开始与新的 Leader 进行数据同步。

重点解析:为什么 Zxid 和 myid 越大越容易成为 Leader?

1. 为什么 Zxid 越大越优先?(核心原因:数据一致性)

Zxid 的本质是数据版本号。Zxid 越大,代表该节点处理过(或应该拥有)越新的事务。

  • 根本目标:分布式系统的核心目标之一就是保证数据一致性,避免数据丢失或回退。
  • 灾难场景:如果让一个 Zxid 很小的节点(数据很旧)当上了 Leader,会发生什么?
    • 这个新 Leader 会开始广播新的事务,它的本地日志会增长。
    • 但那些 Zxid 比它大的 Follower 节点,拥有一些这个新 Leader 不知道的、已提交的事务
    • 根据 ZAB 的崩溃恢复协议,新 Leader 会要求 Follower 与自己同步,这可能导致 Follower 上那些更新的数据被旧 Leader 的日志覆盖而丢失。这是绝对不允许发生的。

因此,选举时优先选择 Zxid 最大的节点,是为了保证“拥有最全数据的节点成为 Leader”,从而杜绝数据丢失,这是 ZAB 协议安全性的基石。

为什么 Zxid 比较小,就要把票更新?
正是因为上述原因。当你发现另一张选票推举的候选人拥有更大的 Zxid(更新的数据)时,你必须“识时务”地改变你的支持对象。你手中的那一票投给数据旧的节点,是在助长“数据丢失”的风险。为了整个集群的数据安全,你必须立刻“倒戈”,把票投给数据更全的那个候选人,并告诉其他同伴也这样做。

2. 为什么 myid 大是次要优势?(辅助原因:快速收敛)

myid 是一个预设的、唯一的服务器标识符,与数据无关。

  • 作用场景:当两个或多个候选节点的 Zxid 一模一样 时,证明它们的数据完备性是完全相同的。
  • 解决的问题:在这种情况下,选谁当 Leader 在数据层面都是安全的。那么,就需要一个确定性的、无歧义的规则来快速选出 Leader,避免集群陷入投票僵局。
  • 为什么是 myid 大? 这是一个简单有效的规则。它不关心服务器的性能或网络位置,只关心一个预设的 ID。选择 myid 最大的,可以确保所有节点在遇到 Zxid 相同的情况时,都会做出同样的选择,从而迅速达成共识。

总结

  • Zxid 是硬指标,决定了谁能当Leader(数据最全者胜),保证了安全性
  • myid 是软指标,决定了在资格相同时谁优先,保证了选举效率确定性
  • “更新选票” 的机制是确保这个“数据最全者胜”的规则能够像潮水一样在集群中扩散开,最终让所有诚实节点都汇聚到同一个正确的候选人身上。

raft协议的区别

好的,这是对 ZAB 协议和 Raft 协议区别的总结。

分点总结

  1. 核心目标与设计出发点

    • ZAB: 为 ZooKeeper 量身定做,其主要目标是保证操作的顺序性,以实现分布式协调服务(如锁、配置管理)的强一致性。
    • Raft: 一个通用的、易于理解的分布式共识算法,其目标是安全地复制一个状态机,适用于各种需要共识的场景。
  2. 状态机复制核心

    • ZAB: 核心是广播状态变化,即“日志条目”本身就是最终状态的变化量。它更关注将一系列状态更新按顺序广播出去。
    • Raft: 核心是复制日志,日志条目是状态机执行的命令。它强调日志本身的一致性,只要日志一致,状态机最终状态就一致。
  3. 日志允许的差异与提交方式

    • ZAB: 在恢复阶段,新 Leader 会覆盖 Follower 上与自己不同的日志。它只提交一个提案序列,不要求所有节点的日志在提交时完全一致。
    • Raft强制保持日志一致性,新 Leader 不会覆盖 Follower 的日志,而是通过一致性检查找到最后一致的点,然后追加新日志。它提交的是具体的日志条目。
  4. Leader选举的约束条件

    • ZAB: 选举时主要比较 zxid(先 epoch,后计数器),拥有最新数据的节点zxid 最大)更可能成为 Leader,这减少了数据丢失风险,是实现Primary-Backup模型的关键。
    • Raft: 选举时除了需要获得多数派投票,还对候选人的日志新旧有要求(Term 更大或 Term 相同但索引更大)。它保证了 Leader 一定包含所有已提交的日志条目,是实现State Machine Replication的关键。
  5. 术语与可理解性

    • ZAB: 术语与流程紧密耦合在 ZooKeeper 的实现中(如 zxid, Proposal, Commit),相对复杂。
    • Raft: 被设计得易于理解,其概念(Leader, Term, Log Index)清晰,模块化(领导选举、日志复制、安全性)明确,便于教学和实现。

区别对比表格

对比维度 ZAB 协议 Raft 协议
核心设计目标 为 ZooKeeper 实现原子广播保证操作顺序 通用的分布式共识,管理复制状态机
设计出发点 服务于特定的协调服务(如发布/订阅、锁) 构建一个易于理解和实现的通用共识模块
状态机复制核心 广播状态变化(日志是状态增量) 复制日志命令(日志是状态机输入)
日志处理方式 恢复阶段,Leader 可以覆盖 Follower 的日志 强制日志一致性,通过追加方式同步日志
提交机制 提交一个提案序列 提交具体的日志条目
Leader选举约束 主要看谁的数据最新zxid 最大) 需要多数派投票,且候选人日志必须足够新
核心标识 Zxid (64位: epoch + 计数器) Term (任期号) + Log Index (日志索引)
术语与复杂度 与 ZooKeeper 实现耦合,相对复杂 模块清晰,术语通俗,易于理解
典型应用 Apache ZooKeeper ETCD, Consul, TiKV

核心思想差异总结:ZAB 协议更像一个 主-从复制(Primary-Backup) 协议,其核心是让备份节点(Backup)严格按顺序执行主节点(Primary)的状态更新指令。而 Raft 是一个标准的 状态机复制(State Machine Replication) 协议,其核心是保证所有节点从同一个初始状态开始,按相同顺序执行相同的命令,从而得到相同的最终状态。

好的,这个关于投票通信模式的观察非常准确,是 ZAB 和 Raft 在选举机制上一个显著且重要的区别。

ZAB 与 Raft 在选举通信模式上的区别

这个区别的核心在于:Raft 使用请求-响应式的“拉票”模型,而 ZAB 使用广播式的“散票”模型。


Raft 的“放射状”投票(拉票模型)

  1. 发起方式

    • 当一个 Follower 的选举超时器到期后,它会自增任期号,并将自己转换为候选人状态。
    • 然后,它向集群中的每一个其他节点,并行地发送 RequestVote RPC(请求投票RPC)
  2. 通信模式

    • 这个过程就像候选人以自己为中心,向所有其他节点放射性地发出拉票请求
    • 每个请求都是一个独立的 RPC 调用,候选人等待每个节点的单独响应。
  3. 投票决策

    • 其他节点收到 RequestVote RPC 后,根据其日志新旧度和当前任期,独立决定是否给这个候选人投票
    • 如果同意,就投出自己的一票;如果拒绝,则告知原因。
  4. 结果统计

    • 候选人自己统计收到的同意票数。如果收到超过半数的同意票,它就晋升为 Leader。

特点总结:Raft 的选举是候选人主动去“拉”票,整个通信流程是由候选人单向发起的请求-响应流,呈现出“放射状”。


ZAB 的“网状”投票(散票模型)

  1. 发起方式

    • 当节点进入 LOOKING 状态后,它会创建一张包含自己投票信息(推举的 leader, Zxid, epoch)的选票
    • 然后,它将这张选票广播给集群中的所有其他节点。同时,它自己也准备好接收来自所有其他节点的选票。
  2. 通信模式

    • 这是一个全互联的、多对多的广播通信。每个节点都同时在做两件事:广播自己的票,接收别人的票。
    • 信息流不是单向的请求-响应,而是所有节点之间相互交换和更新选票,形成一个网状的信息传播网络
  3. 投票决策与更新

    • 节点收到别人的选票后,不是简单地回答“是”或“否”,而是根据我们之前讲的“两轮比较”规则,决定是否要更新自己当前所持的选票
    • 如果决定更新,它会立即将这张更新后的新选票再次广播出去。这个过程是迭代和传染式的。
  4. 结果确认

    • 节点通过观察这个网状通信中流动的选票,来被动地发现哪个 myid 正在获得超过半数的支持。当它发现自己持有的选票所指派的 Leader 已经获得多数派时,选举结束。

特点总结:ZAB 的选举是节点之间不断地“散”播和“更新”票,选票信息在节点组成的网中流动和演化,直到收敛到一个结果,呈现出“网状”。


对比表格与核心思想差异

特性 Raft (放射状/拉票) ZAB (网状/散票)
通信模型 请求-响应 广播-更新
信息流 候选人 → 所有节点 (放射状) 所有节点 ↔ 所有节点 (网状)
投票行为 对候选人的拉票请求做出一次性决定 根据接收到的信息不断更新自己的投票
核心逻辑位置 投票者手中:投票者根据规则决定是否同意。 选票本身和比较算法中:节点只是规则的执行者。
比喻 总统竞选:候选人到处演讲拉票,选民投出固定的一票。 推举盟主:江湖中人不断互通消息, consensus 在交流中逐渐形成,最后大家发现“天下英雄,唯使君与操耳”。

核心思想差异

  • Raft 的设计哲学是清晰和模块化。它的选举过程像一个严格的投票程序:你申请,我审核,我投票。逻辑封装在每一个投票者的决策中,易于理解和实现。
  • ZAB 的设计哲学是高效收敛和对数据一致性的极致追求。它的网状模型允许投票信息快速传播,并且“更新选票”机制确保了拥有最新数据的节点能够像磁铁一样,把所有选票都吸引过来。这个过程虽然看似复杂,但能非常高效地在动态网络中确保最合适的节点成为 Leader。

问题总结:

  1. 什么是zookeeper?

好的,简单来讲:

ZooKeeper 是一个分布式的、开源的协调服务,它相当于分布式系统的“后台管理员”或“神经中枢”。

它的核心功能是为分布式应用提供一些最基础但至关重要的服务,让开发人员不用重复造轮子。


一个生动的比喻:餐厅的后台管理员

想象一个大型餐厅的后厨:

  • 厨师们(分布式应用中的各个节点)需要协同工作。
  • 他们需要知道今天什么菜不能做(配置信息)。
  • 他们需要排队有序地使用烤箱,避免冲突(分布式锁)。
  • 需要知道哪个订单由哪个主厨负责(主节点选举)。

ZooKeeper 就像是这个后厨的后台管理员。厨师们有任何协调需求,都去找这个管理员。管理员维护着一个共享的备忘录(ZooKeeper 的数据模型),所有厨师都可以查看和申请修改这个备忘录,从而达成协同。


ZooKeeper 的核心特点

  1. 数据模型:类似文件系统的树形结构

    • 它维护着一个类似于小型文件系统的树状节点,这些节点叫做 Znode
    • 你可以通过路径(如 /Services/OrderService)来访问这些 Znode。
  2. 保证分布式一致性

    • ZooKeeper 集群自身由多个服务器组成,使用我们之前讨论的 ZAB 协议来保证数据在所有服务器上的一致性。
    • 客户端无论连接到集群中的哪台服务器,看到的数据视图都是一样的。
  3. 提供核心协调服务

    • 配置管理:可以将系统的通用配置(如数据库地址)写入一个 Znode,所有应用节点监听它,配置一变,大家都能即时感知。
    • 命名服务:通过路径地址来定位资源或服务。
    • 分布式锁:多个客户端可以竞争创建一个“临时”Znode,创建成功的那个就相当于获得了锁。
    • 集群管理:可以利用“临时节点”来监控节点的存活状态。节点下线,其创建的临时节点会自动消失,其他节点就能立刻知道。
  4. 可靠性与顺序性

    • 顺序一致性:来自客户端的更新请求会严格按照其发送顺序被应用。这是实现分布式锁等功能的基石。
    • 高可用性:只要集群中超过半数的服务器存活,整个服务就是可用的。

简单来说,ZooKeeper 不是一个用于存储海量业务数据的数据库,而是一个专门用来解决分布式系统中共性协调问题的工具。它通过一个简单的树形数据模型和一套高效的协议,为构建复杂的分布式系统(如 Hadoop, Kafka, HBase 等)提供了坚实可靠的底层支撑。