分布式共识-ZAB协议分析
概述
ZAB(ZooKeeper Atomic Broadcast)协议是专为 ZooKeeper 设计的一种支持崩溃恢复的原子广播协议,是 ZooKeeper 保证分布式数据一致性的核心算法。其核心原理是通过一个主备模型来处理所有事务性请求,并确保这些请求以全局一致的顺序在所有节点上执行和提交。ZAB 协议主要包含两种基本工作模式:消息广播(Message Broadcast)和崩溃恢复(Crash Recovery),并通过三个核心阶段来要求每个 Leader 的工作:发现(Discovery)、同步(Synchronization)和广播(Broadcast)。
核心概念
ZAB 协议运行在一个由奇数个服务器组成的集合(法定人数,Quorum)之上,以保证在部分服务器宕机时仍能进行领导者选举和数据同步。服务节点主要有三种角色:
- 领导者 (Leader):负责处理所有客户端的写请求,并将数据变更(事务)以广播的形式同步给所有 Follower。其核心职责如下:
- 事务处理的唯一核心:在一个 ZooKeeper 集群中,同一时间只存在一个领导者。它负责接收并处理所有改变系统状态的客户端写请求(事务)。
- 生成全局唯一的事务 ID (zxid):领导者为每个事务分配一个全局唯一且严格递增的 64 位事务 ID(zxid)。zxid 的高 32 位是 epoch(纪元),代表领导者的任期;低 32 位是一个单调递增的计数器。这种结构确保了所有事务的全局有序性。
- 发起提案和提交:领导者将带有 zxid 的事务作为提案(Proposal)广播给所有跟随者,并负责收集确认(ACK),在收到法定数量(Quorum)的确认后,再广播提交(Commit)消息。
- 跟随者 (Follower):接收 Leader 的事务提议 (Proposal),参与投票,并在 Leader 确认提交后执行事务。其核心职责如下:
- 接收并处理领导者的提案:跟随者接收来自领导者的事务提案,并将其持久化到本地的事务日志中。
- 参与投票:在持久化提案后,跟随者会向领导者发送 ACK,表示自己已成功接收并记录该提案,这是投票过程的一部分。
- 提交事务:接收到领导者的 Commit 消息后,跟随者才会将对应的事务应用到自己的内存状态机中,使其对客户端可见。
- 处理读请求:跟随者可以直接处理客户端的读请求,从而分担领导者的压力,提高系统的读性能。
- 观察者 (Observer):接收 Leader 的事务提议,执行事务,但不参与投票过程。其核心职责如下:
- 增强读性能的特殊角色:观察者接收并同步来自领导者的事务,但它们不参与提案的投票过程,也不构成 Quorum 的一部分[1]
- 扩展集群的读能力:引入观察者的主要目的是在不影响写性能的情况下,扩展系统的读能力。因为增加跟随者会增加领导者在广播和收集 ACK 时的开销,而增加观察者则不会。
ZAB 协议的运行时,集群中的每个节点(服务器)都会处于以下四种状态之一:
- LOOKING (选举状态):这是集群启动时或 Leader 崩溃、失联时所有节点最初进入的状态。在此状态下,节点之间会相互通信,进行 Leader 选举投票,试图选出一个新的 Leader。状态切换:
- 从LOOKING到LEADER:如果一个节点在选举过程中收到超过半数节点的支持,它将成为Leader,切换到LEADER状态。
- 从LOOKING到FOLLOWER:如果一个节点在选举过程中成为某个Leader的追随者,它将进入FOLLOWER状态。
- FOLLOWING (跟随状态):当节点发现已经选举出 Leader 后(可能是它自己投选的,也可能是收到了多数节点的确认消息),它会切换到此状态,成为一个 Follower。Follower 负责接收 Leader 的提案(Proposal)、记录事务日志、参与投票确认,并向 Leader 汇报心跳和状态。状态切换:
- 从LOOKING到FOLLOWER:在Leader选举阶段,节点会进入LOOKING状态,并根据选举过程的结果,切换到FOLLOWER状态,成为某个Leader的追随者。
- 从LEADER到FOLLOWER:当Leader节点崩溃或进行切换时,Follower节点通过Leader选举机制选举一个新的Leader,切换为新的Leader的追随者。
- LEADING (领导状态):被选举为 Leader 的节点进入此状态。Leader 负责处理所有客户端的写请求,将请求转换为事务提案并广播给 Follower,并在获得多数确认后通知提交。状态切换:
- 从LOOKING到LEADER:当节点在选举过程中(LOOKING状态)收到大多数节点的投票,并且确认自己作为Leader时,就会进入LEADER状态。
- 从FOLLOWER到LEADER:如果Follower节点在Leader崩溃时被选为新的Leader,它会从Follower状态切换为Leader状态。
- OBSERVING (观察状态):观察者(Observer)是 ZooKeeper 集群中的一种特殊角色,它可以接收客户端连接和读请求,将写请求转发给 Leader,不参与 Leader 选举投票和事务的投票确认过程。观察者通常由管理员配置,在集群中明确指定为观察者,并且与Follower节点状态不直接互换。观察者节点不参与Leader选举,因此它们不会从LOOKING状态直接转换为LEADER状态。
ZAB协议的工作原理
ZAB协议是Zookeeper为了保证高可用性和强一致性而设计的协议,它通过Leader选举、日志广播、数据一致性和故障恢复机制,确保了分布式系统中数据的一致性和可靠性。
Leader选举
当集群启动或 Leader 出现故障时,所有节点会进入 LOOKING 状态,并通过 Fast Leader Election (FLE) 算法开始新一轮的 Leader 选举。核心流程如下:
- 自增选举轮次(Epoch):每个参与选举的节点会自增本地的逻辑时钟(logicalClock 或 electionEpoch),标识新一轮选举的开始。
- 发送初始投票:每个节点将自己的服务器 ID(sid)和其记录的最新事务 ID(zxid)封装为一个初始选票,广播给集群中的其他所有节点,每个节点默认投给自己。
- 接收与处理选票:
- 节点会持续接收来自其他节点的选票,并将这些选票放入一个接收队列中。
- 收到选票后,节点会拿自己的当前投票与收到的投票进行比较。首先会比较zxid,拥有更大 zxid(即数据越新)的节点更有资格成为 Leader;如果 zxid 相同,则比较sid,拥有更大 sid 的节点获胜(这是一个平局打破规则)。
- 如果收到的选票优于自己的当前选票,节点会更新自己的投票,并将新投票广播给集群中其他节点。
- 统计选票与确定 Leader:当某个节点的选票信息(包括它自己投给自己的,以及其他节点同意的)获得集群中半数以上节点的支持时,该节点就被确定为“准 Leader”,这个多数共识的机制确保了选举出的 Leader 拥有最新的已提交数据。
- 状态转换与数据同步:
- 一旦确定了 Leader,所有节点退出 LOOKING 状态,进入恢复模式的后续阶段,其他参与投票的节点切换至 FOLLOWING 状态,Observer 切换至 OBSERVING 状态。
- Leader 强制所有 Follower 将数据同步到与自己一致的状态(SYNCHRONIZATION 阶段)。当半数以上的 Follower 完成同步后,集群才正式进入 消息广播(BROADCAST) 模式,开始对外提供正常的读写服务。
原子广播(事务)
ZAB 协议的原子广播(Atomic Broadcast)是其在正常运行阶段(广播阶段)保证数据一致性和全局有序性的核心机制。它确保所有节点以相同的顺序接收和提交所有事务性消息,实现了一种全序广播(Total Order Broadcast)。ZAB 的广播过程类似于一个简化的两阶段提交(2PC),移除了中断逻辑,依赖 Leader 的崩溃恢复机制来处理异常情况,整个过程基于 Leader-Follower 模型进行:
提案生成 (Propose):当客户端发送写请求,这时写请求会被转发到 Leader 节点。Leader 接收到写请求后,不会立即执行,而是生成一个全局唯一的 ZXID (ZooKeeper Transaction ID),即提案ID,ZXID 是一个 64 位整数,高 32 位代表 Leader 的周期(Epoch),低 32 位代表事务递增计数器,ZXID 的单调递增性确保了事务的全局有序性。最后,Leader 会将这个提案(包含 ZXID 和具体数据变更)通过 FIFO(先进先出)的 TCP 通道广播给所有 Follower 节点。
追随者确认 (Acknowledge - Ack):Follower 接收到 Leader 的提案后,会将其写入本地的事务日志(预提交)。写入成功后,Follower 会向 Leader 发送一个确认(Ack)消息,表示它已经记录了这个提案,并准备好提交。
领导者提交 (Commit):Leader 会收集来自 Follower 的确认消息,当 Leader 收到半数以上(quorum)Follower 的确认后,即认为该提案可以安全提交。在完成确认可以提交后,Leader 会向所有 Follower 广播一个 Commit 消息,要求所有Follower节点提交该 ZXID 对应的事务。同时,Leader 也在本地提交该事务,并响应客户端。
ZAB原子性、持久性、顺序性保证:ZAB 事务的“原子性”和“持久性”完全依赖于 “收到半数以上 (Quorum) 的 Ack” 这一机制。
原子性保证:原子性保证主要依赖ACK机制,如果 Leader 成功收到了 Quorum 的 Ack 并发送了 Commit,那么这个事务一定会被提交。即使 Leader 宕机,新选出的 Leader 也必然包含这个已提交的事务(因为选举规则要求新 Leader 必须拥有最新数据,而 Quorum 中至少有一个节点存有该事务)。如果 Leader 在收到 Quorum Ack 之前就宕机了,那么这个事务被视为从未发生过,新 Leader 也不会包含它,最终它会被丢弃。
持久性保证: Follower 必须在发送 Ack 之前,将事务 Proposal 写入磁盘上的事务日志 (WAL)。这确保了即使 Follower 宕机重启,它也能通过日志恢复该事务,不会丢失已确认的数据。
顺序性保证:ZAB 事务的严格顺序性由 Zxid (ZooKeeper 事务 ID) 保证。
- 全局有序:Leader 为每个事务分配一个全局递增的 Zxid (高 32 位是 Leader 周期 Epoch,低 32 位是计数器)。
- 按序应用:所有节点 (Leader 和 Follower) 必须严格按照 Zxid 的顺序将事务应用到内存数据树 (DataTree) 中。如果一个节点发现它收到的 Commit (例如 Zxid=5) 在它已执行的 Zxid (例如 Zxid=3) 之后,但它缺少 Zxid=4,它会等待 Zxid=4 的 Commit 到达并执行后,才会执行 Zxid=5。
总的来说:ZAB 原子广播的核心在于基于多数派确认的二阶段提交和利用 ZXID 保证全局有序,并通过 Leader 选举和数据同步的崩溃恢复机制来弥补简化二阶段提交模型中移除中断逻辑带来的数据不一致风险。
故障恢复
ZAB 协议的故障恢复机制是其保证分布式数据一致性的关键,旨在确保在 Leader 崩溃、网络分区或节点重启等异常情况下,集群能够恢复到一个一致的状态,并继续提供服务。主要由以下三个紧密衔接的阶段组成:
- 发现阶段 (Discovery) - 领导者选举:当集群启动时、Leader 宕机时,或者集群中少于半数节点与 Leader 保持正常通信时,集群会进入 LOOKING 状态,启动新一轮的 Leader 选举,目标是选举出一个新的 Leader,该 Leader 必须拥有集群中最新已提交的事务记录(即最大的 ZXID)。选举机制从根本上保证了所有已经提交的事务都不会丢失,因为最新的数据副本被选为了 Leader。
- 同步阶段 (Synchronization) - 数据同步:新 Leader 选举产生后,进入同步阶段。此时,集群还没有准备好处理新的写请求。这个阶段是Leader强制所有 Follower 的数据状态与新 Leader 的数据状态保持一致。这个阶段 ZAB 协议需要确保两件事:
- 已提交事务的最终提交: 故障发生前在旧 Leader 上已提交(但部分 Follower 未收到 Commit 消息)的事务,会在同步阶段被新 Leader 强制提交到所有节点。
- 未提交事务的丢弃: 故障发生前在旧 Leader 上未完成半数确认的事务,在新 Leader 上不会被提交,而是被丢弃,Follower 会删除这些事务日志。
- 广播阶段 (Broadcast) - 恢复正常运行:只有当集群中超过半数的节点完成了与新 Leader 的数据同步后,ZAB 协议才会退出恢复模式,进入正常的消息广播模式。当进入到广播阶段后,会恢复对外提供正常、一致的读写服务。
总之:ZAB 的故障恢复机制通过选举拥有最新数据的节点和强制全量数据同步这两个步骤,保证了无论发生何种崩溃,集群最终都能达到一个全局一致的状态,并且不会丢失任何已提交的数据。
安全性保证
ZAB 的安全性保证由 原子广播机制(基于多数派的两阶段简化提交)和崩溃恢复机制(基于最新日志的 Leader 选举和数据同步) 共同实现:
- 强一致性与数据完整性:
- 原子性:ZAB 协议提供了强一致性(Strong Consistency),所有的事务提案要么被集群中的所有节点提交,要么一个都不提交,不存在部分节点提交而其他节点不提交的情况。在事务中,
- 持久性:一旦 Leader 确认一个事务已被提交(即获得了多数派确认),那么该事务的状态将是持久的,即使 Leader 宕机,新的 Leader 也会确保所有节点最终提交该事务。
- 全序性与因果一致性 (Total Order and Causal Order):ZAB 协议确保所有节点以相同的、确定的顺序应用事务,这是实现复制状态机(Replicated State Machine)的关键。
- 全序性(Total Order): 如果一个服务器先交付(提交)了消息 A,再交付消息 B,那么集群中的所有其他服务器也必须按照先 A 后 B 的顺序交付这两个消息。ZAB 通过 Leader 统一分配 ZXID(事务 ID)来保证这个全局唯一的顺序。
- 因果顺序性(Causal Order): 如果一个事务 B 是在事务 A 提交后才发起的(A导致B),那么在所有节点上,A 一定在 B 之前被处理和提交。
- 领导者选举的安全性:Leader 选举机制是故障恢复的核心,其安全性保证是整个系统安全的基础。
- 选举的正确性: 选举出的新 Leader 必须是那个拥有集群中最完整、最新已提交事务日志的节点(即拥有最大的 ZXID)。
- 防止旧数据重新出现: ZAB 协议确保丢弃那些在旧 Leader 上未完成提交的事务提案,防止未提交的、可能导致不一致的数据再次出现并污染集群状态。
总结
ZAB协议是Zookeeper为了保证高可用性和强一致性而设计的协议,它通过Leader选举、日志广播、数据一致性和故障恢复机制,确保了分布式系统中数据的一致性和可靠性。ZAB协议的两阶段提交机制,结合Leader的控制,能够有效地保证在故障恢复时数据不丢失。