想象你住在一个小区里:物业经理负责把最新通知贴到公告栏,四位楼栋管家各自抄写一份,再转告给住户。只要大家看到的是同一份通知、顺序完全一致,小区就能平稳运转。可一旦经理突然出差,谁来发布通知?新经理怎么知道以前发过哪些?要是有人贴了一半就离开,又该怎么补救?这一连串问题,正是分布式系统每天都要面对的“协调难题”。 ZooKeeper 就是分布式世界里的“物业公告栏”,它用一棵 ZNode 树存放所有公共信息,用 ZAB 协议保证“公告”在每栋楼里一字不差、顺序不乱。本文就从这块公告栏讲起,带你搞懂它的数据模型、消息广播、故障恢复和 ZXID 编号机制。

岗位关联

掌握 ZooKeeper 数据模型与 ZAB,是架构师在高并发系统中做配置管理、选主与故障恢复的基本功,也是面试中斩获“高可用设计”高分的关键。

目录
  1. 可视化演示
  2. 协调服务概述
  3. ZNode数据模型
  4. ZAB广播机制
  5. 崩溃恢复选举
  6. ZXID序号机制
  7. 典型应用场景
  8. 性能边界与工程实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

观察ZNode树形命名空间结构、ZAB协议Leader广播与崩溃恢复选主过程,理解分布式协调的强一致性与高可用机制,可切换三种模式。

本节配有交互动效(进入视口后自动加载,约 3KB)。

ZNode树形命名空间 数据以类似文件系统的树形层次组织 每个节点ZNode可含临时/顺序/持久属性 命名空间 = 树形 ZNode 客户端可读写并注册Watch。

协调服务概述

一个小区如果没有统一公告栏,物业经理只能挨家挨户敲门通知。有人听到涨价、有人没听到,有人先知道、有人后知道,业主群立刻吵成一锅粥。分布式系统也一样——几十台机器各自运行,谁当主节点、数据库地址是多少、功能开关开没开,这些“小纸条”必须有一个权威的地方发布,大家才能对齐口径。

ZooKeeper 就是分布式世界的“物业公告栏”。它由 3 台或 5 台机器组成一个集群,对外提供统一的数据读写与协调能力。客户端(住户)不直接互相对话,而是都去公告栏读状态,把“谁说了算”的问题交给公告栏解决。集群里只有一台 leader(物业经理)负责发起写操作,其余 follower(楼栋管家)负责同步数据。

写操作必须得到超过半数机器的确认才生效,这就是 Quorum 多数派机制。为什么是过半而不是全部?因为只要过半,任何两个“多数派”就必然有交集,即使有机器撒谎或宕机,正确信息也一定存活在多数派里。

你看,这个小区的公告栏之所以让人放心,不是因为它贴得够多,而是因为“每个消息都有过半管家背书”。这就是 ZooKeeper 一致性的地基。

R+W>N\displaystyle R + W > N
R 是读操作访问的副本数,W 是写操作确认的副本数,N 是总副本数。只要读写访问的副本集合有重叠,客户端就不会读到旧数据。
  1. R读操作的副本数:客户端读取数据时要同时访问 R 台机器,R 越大越不容易漏掉最新值。
    RR
  2. W写操作的副本数:一次写入必须成功落到 W 台机器上才能算完成。
    WW
  3. N集群总机器数:ZooKeeper 集群一共部署了多少台服务器。
    NN
  4. 组合条件 R + W > N读写两边的副本集合必然相交,所以读操作一定能碰到刚写入的那份数据。
    R+W>NR + W > N

ZNode数据模型

公告栏不能把所有通知糊在一张大纸上,得按年份、类别归档:2024/物业费、2024/停水通知。否则贴到第 100 张时,连经理自己都找不到上次的通知在哪。ZooKeeper 的数据模型正是一棵“归档树”。

这棵树由 ZNode 组成,每个节点都有唯一的路径,比如 /app/config。ZNode 内部不仅存数据,还带一串元信息(Stat):数据版本号、时间戳、子节点数量等。树形结构让配置天然具备“命名空间”:不同业务各挂一棵子树,互不干扰。

ZNode 有四种经典类型:持久节点(没人删就一直在)、临时节点(会话断了自动消失)、顺序节点(末尾自动附加递增序号)、以及它们的组合。此外,客户端可以对节点设置 Watch 监听,节点一变化就收到通知——就像住户在公告栏上画个记号,内容一更新管家就会来敲门。

你看,公告栏的每个文件夹都标着“第几版”,住户想看改动历史,翻版本号一目了然。数据模型虽简单,但版本号加树形路径,已经足够支撑复杂的协调业务。

vnew=vold+1\displaystyle v_{new} = v_{old} + 1
每次写入都会让 ZNode 的版本号加一。客户端可以拿着旧版本号做条件写入,版本对不上就失败,这就是乐观锁。
  1. v_old节点当前版本号:ZNode 的 Stat 里记录的数据版本,写入前必须先读出。
    voldv_{old}
  2. 加一操作版本号只能递增绝不回退,保证每次修改都有痕迹。
    +1+1
  3. v_new更新后的版本号:写入成功后,新版本号写回 Stat,作为下一次条件更新的依据。
    vnewv_{new}

ZAB广播机制

经理宣布新通知时,不是直接贴出去就完事——他要把内容先发到每位楼栋管家的手机里,管家逐条回复“收到”,超过半数回复后,经理才喊一声“正式生效”。要是只有一两个人收到就宣布,其他楼栋就乱了套。

ZAB(ZooKeeper Atomic Broadcast,原子广播)就是这个流程的工程实现。所有写请求由 leader 统一接收,然后分三步走:第一步,leader 把请求包装成 proposal(提案)发给所有 follower;第二步,follower 把提案写入本地日志后回 ACK 确认;第三步,leader 收到超过半数的 ACK 后,广播 commit 提交指令,数据正式生效。

ZAB 的好处是顺序严格:所有提议按 ZXID 大小排队,先提交的绝不会晚于后提交的生效。因此客户端看到的事件顺序全局一致,不会出现“A 节点执行了写入 1,B 节点却先执行了写入 2”的乱象。

所以 3 台能坏 1 台、5 台能坏 2 台,这就是多数派的数学底气。公告栏之所以稳,不是因为管家多,而是因为“过半确认”这条铁律无论在平时还是故障时都成立。

n≥2f+1\displaystyle n \geq 2f + 1
要容忍 f 台机器同时宕机,集群至少需要 2f+1 台机器。这样任意多数派里总会剩下至少一台健康机器。
  1. f可容忍的故障数:我们希望集群同时坏掉 f 台机器时依然能选举和提交。
    ff
  2. 2f + 1最小部署数:只有总数达到 2f+1,多数派才大于 f,才能保证“多数派必含健康节点”。
    2f+12f + 1
  3. n实际机器数:ZooKeeper 部署时按这个公式选奇数台,比如容忍 1 台故障就部署 3 台。
    n≥2f+1n \geq 2f + 1

崩溃恢复选举

经理突然请长假,小区不能停摆,必须马上选出新经理。选谁?有经验的业主都知道:谁的记录本最全,谁最合适。记录本里最新公告编号最大的管家,说明他抄写最勤快、信息最完整,就先让他上位。

ZAB 的崩溃恢复分三个阶段:选举、数据同步、消息广播。选举时,每个节点把自己那票投给“日志最新”的节点,获得超过半数选票者成为新 leader;随后新 leader 检查所有 follower 的日志,把缺失的历史提案重新广播补齐;一切同步完成后才恢复正常对外服务。

这里有个容易踩坑的细节:旧 leader 恢复后,绝不能自动恢复领导身份。它的日志可能落后于新 leader,强行上位会造成双主分裂。ZAB 规定:旧 leader 重启后只能以 follower 身份加入,先同步新 leader 的数据,再谈其他。

这一整套流程,就像小区在经理缺席时“按记录本补齐、再选新主、旧主归队当副手”,把混乱压到最低。崩溃恢复不是玄学,而是一套可证明的交接制度。

votes(leader)>n2\displaystyle votes(leader) > \frac{n}{2}
候选人必须拿到严格超过半数的选票才能当选。等于半数也不行,因为可能同时出现两个“半数”,造成脑裂。
  1. votes(leader)候选人获得的票数:每台机器把票投给日志最新(ZXID 最大)的节点。
    votes(leader)votes(leader)
  2. n / 2半数阈值:集群总机器数的一半,是区分“多数”与“少数”的分界线。
    n2\frac{n}{2}
  3. 严格大于只有票数严格超过半数的候选人才合法,确保全局只有一个 leader。
    >n2\gt \frac{n}{2}

ZXID序号机制

公告栏的每张通知都盖着一个编号,比如“2024-001“。新经理上任后,年份进位成 2025,序号重新从 001 开始。这样不管换几任经理,任意两张通知都能直接比较先后,不会出现”2024-500“和”2025-001”谁大谁小的争论。

ZooKeeper 的 ZXID 就是这样一个 64 位“公告编号”:高 32 位是 epoch(纪元),低 32 位是 counter(计数器)。每选出一位新 leader,epoch 就加一;在同一任 leader 内,counter 随每个事务单调递增。

为什么不用一个全局自增数?因为崩溃后没有全局时钟,各节点无法确认“我落后的那部分到底缺了多少”。有了 epoch,任何节点一眼就能判断:epoch 大的日志一定更新;epoch 相同则比 counter。选举时直接挑 ZXID 最大的节点,简单又可靠。

你看,公告编号就是这么朴实:年份管换届,流水号管日常。ZXID 让 ZooKeeper 在没有绝对时钟的世界里,依然能对“谁更新”达成共识。

ZXID=(epoch≪32)∣counter\displaystyle ZXID = (epoch \ll 32) \mid counter
将纪元号左移 32 位,再与计数器按位或拼起来,就得到了唯一的 64 位事务编号。
  1. epoch高 32 位纪元号:每轮 leader 选举后加一,代表“这是第几届领导班子”。
    epochepoch
  2. 左移 32 位把纪元号推到高位,为低 32 位的计数器腾出空间。
    ≪32\ll 32
  3. 按位或拼接 counter把当届的递增计数器填到低位,两个字段合二为一。
    ∣counter\mid counter
  4. 整体 ZXID最终得到一个全局单调递增的 64 位编号,既比新旧,又辨顺序。
    ZXIDZXID

典型应用场景

公告栏在小区里最常见的三种玩法:贴统一的装修规范(配置管理)、排今天的值班经理(选主)、登记公共车位(分布式锁)。每一种都不复杂,但组合起来就能解决大问题。

配置管理:把数据库地址、功能开关写进 ZNode,客户端注册 Watch,推送变更时立刻收到通知并刷新本地缓存。选主:多个候选者在同一父节点下创建临时顺序节点,序号最小者就是 leader,其余候选者监听它,leader 一挂立刻接管。分布式锁:同样用临时顺序节点,客户端申请锁时创建序号节点,检查自己是否最小,不是就监听前一个节点释放。

临时节点在其中功不可没——它与会话绑定,客户端崩溃或会话超时,节点自动消失,锁和 leader 身份也随之释放。整个过程不需要人工干预,实现了“故障自愈”。

无论是贴配置还是抢车位,核心都是“用一个权威的小节点 + 序号”来传递状态。公告栏不存大文件,只存“谁说了算”的小纸条,这就是 ZooKeeper 的精髓。

is_leader=(seqi=min⁡(seq1,seq2,…,seqm))\displaystyle is\_leader = \left( seq_i = \min(seq_1, seq_2, \ldots, seq_m) \right)
多个候选者各自创建带序号的临时节点,谁的序号最小谁当选 leader。序号相同不可能,因为序号由集群原子递增分配。
  1. seq_i当前客户端拿到的序号:创建临时顺序节点后,ZooKeeper 会在父节点下追加一个单调递增的编号。
    seqiseq_i
  2. min(seq_1, ……, seq_m)全体竞争者中的最小序号:所有候选者的序号集合,最小者代表“最先申请”。
    min⁡(seq1,seq2,…,seqm)\min(seq_1, seq_2, \ldots, seq_m)
  3. 相等判断只有序号与最小值相等的客户端胜出,自然形成唯一 leader。
    is_leaderis\_leader

性能边界与工程实践

公告栏再牛也有承载上限:贴太多大文件会挡住视线,太多人同时挤着看会堵路。ZooKeeper 的性能边界同样清晰,用好它需要知道这些红线在哪。

首先是吞吐模型:写操作必须经过 leader 单点、写磁盘日志并等待多数派 ACK,所以写吞吐远低于读吞吐;读操作可以分散到任意 follower 处理,因此读可以水平扩展。其次是数据大小:单个 ZNode 默认上限 1MB,但生产环境建议控制在 1KB 以内,否则每一次同步和 Watch 推送都会拖垮网络。

第三是会话超时:客户端与服务器靠心跳保活,超时设得太小,网络抖动就会误杀临时节点;设得太大,故障恢复又变慢。最后是羊群效应:成百上千客户端同时 Watch 一个节点时,节点一变所有客户端同时被唤醒,形成惊群,务必用“连坐式监听”化解。

实践里最常见的坑,就是把 ZooKeeper 当数据库用、把节点塞得又大又满。记住一句话:公告栏只贴小纸条,大文件请走专门的文件系统,这才是在高并发环境里用对 ZooKeeper 的姿势。

A=∑k=f+1nCnkak(1−a)n−k\displaystyle A = \sum_{k=f+1}^{n} C_n^k a^k (1-a)^{n-k}
假设单台机器可用性为 a,集群只要有超过 f 台机器能用就能继续服务,把所有“可用台数”的概率加起来就是集群整体可用性。
  1. a单台机器的可用性:一台机器在单位时间内正常工作的概率,比如 0.99 表示一年约宕机 3.65 天。
    aa
  2. 组合数 C_n^kn 台机器中恰好有 k 台可用的搭配方式数量。
    CnkC_n^k
  3. 累加范围 k=f+1 到 n只要可用的机器数大于 f,集群就能完成选举与提交,所有这类情形都要计入。
    ∑k=f+1n\sum_{k=f+1}^{n}
  4. 整体可用性 A所有“集群可用”情形的概率之和。3 台机器、单机可用性 0.99 时,A ≈ 0.9997,可靠性提升一个数量级。
    AA

AI 应用场景

AI平台的任务选主与心跳协调

阶段:训练任务的分布式协调

解决的问题:大规模深度学习训练常采用多机多卡,需要从一组 Worker 中选出一个 Leader 来聚合梯度、下发任务。传统做法靠人工指定或数据库抢锁,Worker 崩溃后无法自动感知,容易造成训练中断。

具体案例:某 AI 训练平台在 ZooKeeper 的 /train/job-123/leader 下创建临时顺序节点,各 Worker 自检序号,最小者担任 Leader;其余 Worker 监听该节点。Leader 机器宕机后,临时节点随会话超时消失,序号次小的 Worker 在秒级内自动接任,训练任务无感知切换。

模型特征开关的配置中心

阶段:在线推理服务的配置下发

解决的问题:推荐模型的特征版本、模型文件路径、超参数开关散落在各服务节点,人工改配置重启会中断线上流量,且各节点配置不一致会导致推理结果漂移。

具体案例:推理服务启动时从 ZooKeeper 的 /config/recommend/v2 读取特征开关,并注册 Watch。算法团队在后台修改节点数据后,所有推理机几十毫秒内收到变更通知并热加载新配置,全程无重启、无流量损失,A/B 实验的开关切换从此变成“改一个节点”的事。

数据管线的分布式锁与作业防重

阶段:数据预处理管线的并发控制

解决的问题:多个调度器实例可能同时拉起同一个数据清洗任务,导致同一份样本被重复消费。传统的“先查库再插入”存在竞态窗口,需要一把分布式环境下的互斥锁。

具体案例:数据平台让每个任务在 /pipeline/locks/{task_name} 下创建临时顺序节点,只有拿到最小序号的实例能执行任务,其余实例进入监听等待。任务完成后删除节点释放锁,下一实例自动接续,彻底避免重复计算。

Python 演示

python64 行
import random  class ZabNode:    """模拟一个 ZAB 节点,持有最新 ZXID(64 位编号)"""     def __init__(self, node_id, epoch, counter):        self.node_id = node_id        self.zxid = (epoch << 32) | counter     def is_newer_than(self, other):        return self.zxid > other.zxid     def zxid_text(self):        return "0x%016X" % self.zxid  def elect_leader(nodes):    """每台机器把票投给日志最新(ZXID 最大)的节点, 再检查是否过半"""    n = len(nodes)    votes = {}    for voter in nodes:        best = voter        for other in nodes:            if other.is_newer_than(best):                best = other        votes[best.node_id] = votes.get(best.node_id, 0) + 1    for node_id, count in votes.items():        if count > n // 2:            return node_id    return None  def broadcast(leader, followers, proposal_id):    """leader 广播提案, 模拟 10% 丢包, 统计多数派 ACK"""    total = len(followers) + 1    acks = 1    for f in followers:        if random.random() > 0.1:            acks += 1    return acks > total // 2, acks, total  def main():    random.seed(7)    nodes = [        ZabNode(1, 1, 5),        ZabNode(2, 2, 3),        ZabNode(3, 2, 3),        ZabNode(4, 2, 4),    ]    for node in nodes:        print("node", node.node_id, "zxid:", node.zxid_text())     leader_id = elect_leader(nodes)    print("elected leader:", leader_id)    followers = [n for n in nodes if n.node_id != leader_id]    for i in range(1, 4):        ok, acks, total = broadcast(leader_id, followers, i)        print("proposal", i, "committed:", ok, "acks:", acks, "/", total)  if __name__ == "__main__":    main()
目的:用不到 60 行代码模拟 ZAB 的核心两件事——按 ZXID 选举 leader、按多数派 ACK 决定提案是否提交,直观展示“过半数”规则的作用。
  1. self.zxid = (epoch << 32) | counter把纪元号左移 32 位再拼上计数器,拼出 64 位 ZXID。
  2. if other.is_newer_than(best):谁的 ZXID 大,投票时就选谁当 leader。
  3. if count > n // 2:严格超过半数才算当选,等于半数不合法。
  4. acks = 1广播统计时 leader 自己先算一票,因为提案就在它的日志里。
  5. if random.random() > 0.1:模拟 10% 丢包,看多数派机制如何在网络不稳时“保住”提交。
  6. ok, acks, total = broadcast(leader_id, followers, i)每一轮提案打印是否提交,体会“过半即成功”与“不过半即失败”的分界线。

常见误区

误区ZooKeeper 名字里带个"Keeper"又能存数据,那我把用户资料、订单详情都塞进去,是不是就拥有一个高可用数据库了?

正解ZooKeeper 是协调服务,不是存储引擎。单节点数据默认上限 1MB,生产建议控制在 1KB 以内;写入需要 leader 写磁盘并等多数派 ACK,吞吐远低于 KV 数据库。它最擅长存“配置、状态、锁信息”这类小纸条,而不是大数据。类比公告栏,贴通知足够,存档案得去档案室。

为何常见:ZooKeeper 对外提供树形数据读写接口,看起来很像数据库,初学者容易忽略它“协调优先、存储为辅”的定位。

误区既然 ZAB 保证了强一致,那我随便连哪台机器读,都一定是最新数据,对吧?

正解ZAB 保证的是“顺序一致性”:写操作严格按顺序提交,但普通的读请求可以打到任意 follower,而 follower 的同步存在微小延迟,所以读可能读到旧值。想要读最新,必须调用 sync 操作或直接读 leader。类比公告栏,广播保证每栋楼最终都有同一份通知,但 A 栋贴完 B 栋可能还没贴完,住户此刻去看可能扑空。

为何常见:网上资料常把 ZooKeeper 简称为“强一致协调服务”,省略了“写强一致、读可滞后”的细节。

误区反正要高可用,我部署 10 台机器,坏 8 台还能剩 2 台,肯定比 3 台稳吧?

正解ZooKeeper 的提交和选举都要求超过半数节点确认。10 台机器要 6 台确认,坏 5 台就瘫了;而且节点越多,广播和选举的通信开销越大,写性能反而下降。正确做法是部署奇数台(3、5、7),在可用性与性能之间取平衡。多数派的红线是“过半”,不是“越多越好”。

为何常见:习惯性地把“副本越多越可靠”套用到 ZooKeeper,忽略了 quorum 机制下偶数台既不能提高容错,纯属浪费。

误区临时节点绑定会话,那客户端一旦断网,节点应该立刻被删掉吧?这样锁就能马上释放。

正解临时节点绑定的是会话,而会话有超时时间(session timeout)。客户端断网后,只要在超时时间内恢复心跳,会话和临时节点都完好无损;超过超时时间仍未心跳,服务端才判定会话过期并删除节点。如果把超时设得太短,一次网络抖动就可能误删锁和 leader 身份。

为何常见:把“网络断开”和“会话过期”两件事混为一谈,忽略了中间那段宝贵的“宽限期”。

面试题

Q1. 关于 ZooKeeper 的 ZNode 数据组织方式,正确的是?

单选题 · easy

  • A.以链表结构组织数据
  • B.以树形结构组织数据← 正确答案
  • C.以环形队列组织数据
  • D.以哈希表结构组织数据

答案:B。ZooKeeper 的数据模型是一棵 ZNode 树,每个节点有唯一路径如 /app/config。链表、队列、哈希表都不是它的组织方式。回忆“公告栏归档文件夹”的比喻,树形路径是这套模型最直观的特征。

Q2. 在 ZAB 广播流程中,leader 收到超过半数 ACK 之后要做什么?

单选题 · easy

  • A.向所有从节点再发一遍提案
  • B.向所有从节点发送提交指令← 正确答案
  • C.立即向客户端返回失败响应
  • D.马上放弃自己的领导地位

答案:B。ZAB 三阶段是“提案 → ACK → 提交(commit)”。leader 统计到多数派 ACK 后,广播 commit 指令让数据正式生效。重发提案是第一步的事,返回失败是提案未被接受时的事,放弃领导权则与提交无关。

Q3. 一个 5 台机器的 ZooKeeper 集群,最多能容忍几台机器同时宕机?

单选题 · mid

  • A.最多容忍 1 台宕机
  • B.最多容忍 2 台宕机← 正确答案
  • C.最多容忍 3 台宕机
  • D.最多容忍 4 台宕机

答案:B。集群要正常工作必须满足“存活数 > 总数一半”,即至少 3 台存活。5 台中坏 2 台剩 3 台,可以选出 leader 并完成提交;坏 3 台只剩 2 台,无法构成多数派。公式 n ≥ 2f+1 当 n=5 时 f=2。

Q4. 关于 ZXID 结构的说法正确的是?

单选题 · mid

  • A.高 32 位是 epoch 每次选举加一← 正确答案
  • B.低 32 位是 epoch 用于换届加一
  • C.高 32 位是随机数保证唯一性
  • D.低 32 位是校验位防止篡改

答案:A。ZXID 是 64 位事务编号,高 32 位是 epoch(纪元),每轮选举后加一;低 32 位是 counter,在同一 leader 内随事务递增。随机数和校验位都与 ZXID 无关。类比公告编号“年份-流水号”,年份就是 epoch。

Q5. 临时节点会在下列哪种情况下被 ZooKeeper 自动删除?

单选题 · mid

  • A.客户端断网后立刻被删除
  • B.会话超过超时时间仍未恢复← 正确答案
  • C.客户端进程暂停执行一秒
  • D.节点数据被写入超过 1KB

答案:B。临时节点与客户端会话绑定,只有会话超时(session timeout)后服务端才删除它。断网后只要在超时内恢复心跳,节点不会被删;暂停一秒是否触发删除取决于超时阈值,而不是固定一秒。数据大小限制不影响节点存亡。

Q6. ZooKeeper 中单个 ZNode 生产环境建议存放的数据量是?

单选题 · easy

  • A.越大越好,最大 1GB
  • B.尽量小,通常不超过 1KB← 正确答案
  • C.至少 1MB 才有意义
  • D.与数据量大小没有关系

答案:B。ZooKeeper 是协调服务,ZNode 默认上限 1MB,但生产实践强烈建议控制在 1KB 以内,避免大节点拖慢同步与 Watch 推送。公告栏只贴小纸条,大文件请走对象存储。

Q7. 一个 ZNode 的数据版本号在写入成功后会发生什么?

单选题 · mid

  • A.保持不变,避免多余开销
  • B.自动加一,形成单调递增← 正确答案
  • C.随机重置,防止规律可循
  • D.由客户端指定的任意值

答案:B。ZNode 每次写入成功后,其 Stat 中的版本号自动加一。版本号是乐观锁的基础:客户端带着旧版本号做条件更新,版本不一致就失败。它不会随机重置,也不能由客户端随意指定。

Q8. 以下属于 ZooKeeper 典型应用场景的有?

多选题 · mid

  • A.分布式配置项统一管理← 正确答案
  • B.分布式场景下的选主← 正确答案
  • C.分布式锁与协调会话← 正确答案
  • D.海量图片的持久存储

答案:ABC。配置管理靠 ZNode + Watch,选主靠临时顺序节点,分布式锁靠序号最小者获胜。海量图片是典型的大数据存储场景,应使用对象存储或文件系统,ZooKeeper 只放小数据,不能承担这种职责。

Q9. 关于 ZAB 协议说法正确的有哪些?

多选题 · mid

  • A.写请求由 leader 统一发起提案← 正确答案
  • B.提案需超过半数 ACK 才提交← 正确答案
  • C.所有读请求都强制经过 leader
  • D.广播顺序严格按 ZXID 递增← 正确答案

答案:ABD。ZAB 中写请求必须走 leader,提案要多数派确认才 commit,广播按 ZXID 严格排序。但读请求并不强制经过 leader,普通读可打任意 follower,因此可能读到延迟数据。C 把读路径与写路径混为一谈。

Q10. 提升 ZooKeeper 集群稳定性的合理做法包括?

多选题 · hard

  • A.集群部署为奇数台如 5 台← 正确答案
  • B.节点数据保持 KB 以内← 正确答案
  • C.合理设置会话超时时间← 正确答案
  • D.用偶数台满足多数派要求

答案:ABC。奇数台是 quorum 公式 n ≥ 2f+1 的要求,偶数台如 4 台只比 3 台多一台成本却无法多容忍一次故障;小数据保证同步和推送效率;会话超时过小会误杀临时节点,过大延误故障感知。这三项都是生产实践的金科玉律。