分布式详解2
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 状态,并首先投自己一票。
- 这张选票包含三个核心信息:
- 我当前所推举的 Leader 的 myid(初始时就是自己)。
- 我本地持有的最大 Zxid (
lastZxid
)。 - 我当前所处的逻辑时钟 Epoch。
步骤 2: 节点间交换选票
- 每个处于 LOOKING 状态的节点,都会通过 投票广播 将自己的这张选票发送给集群中的所有其他节点。
- 同时,它也会不断地接收来自其他节点的选票。
步骤 3: 接收选票并进行“两轮比较”
这是整个选举过程的核心。当一个节点收到另一张选票时,它会将收到的选票与自己当前所持有的选票进行比较。比较规则遵循一个严格的优先级,即“数据完整性优先,服务器ID为辅”:
第一轮比较:比较 Zxid (数据新旧)
- 规则:优先比较双方选票中携带的
lastZxid
(即所推举 Leader 的 Zxid)。 - 如果收到的选票的 Zxid > 自己当前选票的 Zxid:
- 这证明对方推举的 Leader 拥有比自己推举的 Leader 更更新的数据。
- 为了保证集群数据的一致性,必须让拥有最全数据的人当 Leader,否则会造成数据丢失或回滚。
- 行动:节点会无条件地更新自己的选票,改为支持对方推举的那个 myid,并将这个更新后的选票再次广播出去。
- 如果收到的选票的 Zxid < 自己当前选票的 Zxid:
- 说明对方的数据更旧,直接忽略这张选票。
- 规则:优先比较双方选票中携带的
第二轮比较:比较 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 协议区别的总结。
分点总结
核心目标与设计出发点
- ZAB: 为 ZooKeeper 量身定做,其主要目标是保证操作的顺序性,以实现分布式协调服务(如锁、配置管理)的强一致性。
- Raft: 一个通用的、易于理解的分布式共识算法,其目标是安全地复制一个状态机,适用于各种需要共识的场景。
状态机复制核心
- ZAB: 核心是广播状态变化,即“日志条目”本身就是最终状态的变化量。它更关注将一系列状态更新按顺序广播出去。
- Raft: 核心是复制日志,日志条目是状态机执行的命令。它强调日志本身的一致性,只要日志一致,状态机最终状态就一致。
日志允许的差异与提交方式
- ZAB: 在恢复阶段,新 Leader 会覆盖 Follower 上与自己不同的日志。它只提交一个提案序列,不要求所有节点的日志在提交时完全一致。
- Raft: 强制保持日志一致性,新 Leader 不会覆盖 Follower 的日志,而是通过一致性检查找到最后一致的点,然后追加新日志。它提交的是具体的日志条目。
Leader选举的约束条件
- ZAB: 选举时主要比较
zxid
(先 epoch,后计数器),拥有最新数据的节点(zxid
最大)更可能成为 Leader,这减少了数据丢失风险,是实现Primary-Backup模型的关键。 - Raft: 选举时除了需要获得多数派投票,还对候选人的日志新旧有要求(Term 更大或 Term 相同但索引更大)。它保证了 Leader 一定包含所有已提交的日志条目,是实现State Machine Replication的关键。
- ZAB: 选举时主要比较
术语与可理解性
- ZAB: 术语与流程紧密耦合在 ZooKeeper 的实现中(如
zxid
, Proposal, Commit),相对复杂。 - Raft: 被设计得易于理解,其概念(Leader, Term, Log Index)清晰,模块化(领导选举、日志复制、安全性)明确,便于教学和实现。
- ZAB: 术语与流程紧密耦合在 ZooKeeper 的实现中(如
区别对比表格
对比维度 | 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 的“放射状”投票(拉票模型)
发起方式:
- 当一个 Follower 的选举超时器到期后,它会自增任期号,并将自己转换为候选人状态。
- 然后,它向集群中的每一个其他节点,并行地发送 RequestVote RPC(请求投票RPC)。
通信模式:
- 这个过程就像候选人以自己为中心,向所有其他节点放射性地发出拉票请求。
- 每个请求都是一个独立的 RPC 调用,候选人等待每个节点的单独响应。
投票决策:
- 其他节点收到 RequestVote RPC 后,根据其日志新旧度和当前任期,独立决定是否给这个候选人投票。
- 如果同意,就投出自己的一票;如果拒绝,则告知原因。
结果统计:
- 候选人自己统计收到的同意票数。如果收到超过半数的同意票,它就晋升为 Leader。
特点总结:Raft 的选举是候选人主动去“拉”票,整个通信流程是由候选人单向发起的请求-响应流,呈现出“放射状”。
ZAB 的“网状”投票(散票模型)
发起方式:
- 当节点进入 LOOKING 状态后,它会创建一张包含自己投票信息(推举的 leader, Zxid, epoch)的选票。
- 然后,它将这张选票广播给集群中的所有其他节点。同时,它自己也准备好接收来自所有其他节点的选票。
通信模式:
- 这是一个全互联的、多对多的广播通信。每个节点都同时在做两件事:广播自己的票,接收别人的票。
- 信息流不是单向的请求-响应,而是所有节点之间相互交换和更新选票,形成一个网状的信息传播网络。
投票决策与更新:
- 节点收到别人的选票后,不是简单地回答“是”或“否”,而是根据我们之前讲的“两轮比较”规则,决定是否要更新自己当前所持的选票。
- 如果决定更新,它会立即将这张更新后的新选票再次广播出去。这个过程是迭代和传染式的。
结果确认:
- 节点通过观察这个网状通信中流动的选票,来被动地发现哪个 myid 正在获得超过半数的支持。当它发现自己持有的选票所指派的 Leader 已经获得多数派时,选举结束。
特点总结:ZAB 的选举是节点之间不断地“散”播和“更新”票,选票信息在节点组成的网中流动和演化,直到收敛到一个结果,呈现出“网状”。
对比表格与核心思想差异
特性 | Raft (放射状/拉票) | ZAB (网状/散票) |
---|---|---|
通信模型 | 请求-响应 | 广播-更新 |
信息流 | 候选人 → 所有节点 (放射状) | 所有节点 ↔ 所有节点 (网状) |
投票行为 | 对候选人的拉票请求做出一次性决定 | 根据接收到的信息不断更新自己的投票 |
核心逻辑位置 | 投票者手中:投票者根据规则决定是否同意。 | 选票本身和比较算法中:节点只是规则的执行者。 |
比喻 | 总统竞选:候选人到处演讲拉票,选民投出固定的一票。 | 推举盟主:江湖中人不断互通消息, consensus 在交流中逐渐形成,最后大家发现“天下英雄,唯使君与操耳”。 |
核心思想差异:
- Raft 的设计哲学是清晰和模块化。它的选举过程像一个严格的投票程序:你申请,我审核,我投票。逻辑封装在每一个投票者的决策中,易于理解和实现。
- ZAB 的设计哲学是高效收敛和对数据一致性的极致追求。它的网状模型允许投票信息快速传播,并且“更新选票”机制确保了拥有最新数据的节点能够像磁铁一样,把所有选票都吸引过来。这个过程虽然看似复杂,但能非常高效地在动态网络中确保最合适的节点成为 Leader。
问题总结:
好的,简单来讲:
ZooKeeper 是一个分布式的、开源的协调服务,它相当于分布式系统的“后台管理员”或“神经中枢”。
它的核心功能是为分布式应用提供一些最基础但至关重要的服务,让开发人员不用重复造轮子。
一个生动的比喻:餐厅的后台管理员
想象一个大型餐厅的后厨:
- 厨师们(分布式应用中的各个节点)需要协同工作。
- 他们需要知道今天什么菜不能做(配置信息)。
- 他们需要排队有序地使用烤箱,避免冲突(分布式锁)。
- 需要知道哪个订单由哪个主厨负责(主节点选举)。
ZooKeeper 就像是这个后厨的后台管理员。厨师们有任何协调需求,都去找这个管理员。管理员维护着一个共享的备忘录(ZooKeeper 的数据模型),所有厨师都可以查看和申请修改这个备忘录,从而达成协同。
ZooKeeper 的核心特点
数据模型:类似文件系统的树形结构
- 它维护着一个类似于小型文件系统的树状节点,这些节点叫做 Znode。
- 你可以通过路径(如
/Services/OrderService
)来访问这些 Znode。
保证分布式一致性
- ZooKeeper 集群自身由多个服务器组成,使用我们之前讨论的 ZAB 协议来保证数据在所有服务器上的一致性。
- 客户端无论连接到集群中的哪台服务器,看到的数据视图都是一样的。
提供核心协调服务
- 配置管理:可以将系统的通用配置(如数据库地址)写入一个 Znode,所有应用节点监听它,配置一变,大家都能即时感知。
- 命名服务:通过路径地址来定位资源或服务。
- 分布式锁:多个客户端可以竞争创建一个“临时”Znode,创建成功的那个就相当于获得了锁。
- 集群管理:可以利用“临时节点”来监控节点的存活状态。节点下线,其创建的临时节点会自动消失,其他节点就能立刻知道。
可靠性与顺序性
- 顺序一致性:来自客户端的更新请求会严格按照其发送顺序被应用。这是实现分布式锁等功能的基石。
- 高可用性:只要集群中超过半数的服务器存活,整个服务就是可用的。
简单来说,ZooKeeper 不是一个用于存储海量业务数据的数据库,而是一个专门用来解决分布式系统中共性协调问题的工具。它通过一个简单的树形数据模型和一套高效的协议,为构建复杂的分布式系统(如 Hadoop, Kafka, HBase 等)提供了坚实可靠的底层支撑。