2026-10-31·by Sijie Wang

ha-and-fencing

Split-brain, fencing order and three methods, JournalNodes as WAL storage, ZooKeeper and ZKFC, hot standby, failover, and CP character.

HA 与 fencing:一个写者、共享日志,和一把定 leader 的锁

脑裂,先下定义。 脑裂是分区两侧都以为自己是 leader、都写:元数据账本分叉成两个互不兼容的版本,事后任何合并都 reconciled 不了。本节点的一切,都是为了让这件事不可能发生。

顺序是定的:先定新 leader,再保证旧 leader 写不了。 fencing 就是在新 leader 动手之前,废掉旧 leader 的写权限。它不是接管之后打的补丁,而是让接管安全的那一步。

fencing 三法。 第一,ssh fencing:远程杀掉旧 active 的进程。SSH 本身不通时就失效,而那恰恰是最需要 fencing 的时候。第二,经 QJM 的 epoch fencing:NameNode 晋升时 epoch(任期)号加 1,JournalNode 只接受最新 epoch 的写,活着的旧 active 也写不进——类比是换了锁,旧钥匙被拒。第三,经 ZKFC 的 ZooKeeper 锁:只有持锁者才能是 active,丢了锁就丢了写的权利。

JournalNode 是职位,不是一台机器。 QJM 组是 3 或 5 个,奇数。active 把 edit 写给多数,standby 从多数读。3 丢 1 毫无影响。丢多数——3 丢 2——则新写停下,因为 active 提交不了 edit,但已提交的数据不丢:JournalNode 回来或替换者同步进来后,写就恢复。JournalNode 盘上只存 edit-log 段,不存整本账。

JournalNode 就是 WAL。 EditLog 就是 NameNode 的 WAL,HA 下它的存储在 JournalNode 组。它只记 active 的元数据变更:不记文件内容、不记 DataNode 数据、不记客户端数据。它也不是 ZooKeeper 自己的事务日志。没有 HA 时,EditLog 在 NameNode 自己的盘上。

ZooKeeper:里面没有针对各系统的适配器。 ZooKeeper 自身不含 HDFS 或任何系统的适配器,适配器在应用一侧。HDFS 里是每个 NameNode 旁的 ZKFC:健康检查本地 NameNode、抢 ZooKeeper 里的锁、赢了就晋升自己的 NameNode 并 fence 旧的。ZooKeeper 自身的原理:3、5 或 7 台的 ensemble,一个 leader、其余 follower,写由 leader 定序、多数确认才提交(ZAB 协议),层级 znode 树存小数据(至多 1MB),持久节点与 ephemeral 节点——ephemeral 节点绑定客户端会话、会话超时即消失,HA 锁就靠它——以及一次性 watch,而不是轮询。

综合,有一个常见误读要纠正。 从 ZooKeeper 拿到的是一个人人同步的小笔记本,走 leader-follower 强一致架构。而一个小的可信控制子集群——2 台 NameNode、一个 ZooKeeper ensemble、一个 JournalNode 组——位于 DataNode 数据平面之上。但 ZooKeeper 笔记本不记 journal:它只记 leader 锁,journal 和 EditLog 在独立的 JournalNode 组。分开的原因是 ZooKeeper 全数据集在内存、只适合小状态,而 JournalNode 是盘上只追加的段存储。历史上 QJM 替代的是存 edit 的单台 NFS 服务器,不是替代 ZooKeeper。

日志一致性是为了 standby 跟上,不是为了告诉谁是 leader。 选举加日志一致性都对,但最后一环常被说错:日志一致性存在,是为了 standby 能跟上,不是为了让谁知道谁是 leader。身份来自 ZooKeeper 锁,数据新旧来自 journal。

standby 就是 HA 对里的另一台 NameNode。 active 和 standby 是状态,不是固定机器;没有 HA 就没有 standby。而且 standby 是热备,持续做三件事:尾随 JournalNode、把每条 edit 重放到自己内存账本(滞后秒级);直接收 DataNode 心跳与块报告,接管时已经知道块位置;定期 checkpoint 合并。它不接受客户端写。

接管。 standby 的 ZKFC 赢得 ZooKeeper 锁、fence 旧 active、补最后 journal edit、翻为 active。客户端用逻辑 nameservice 名并重试。这里的“立刻”是秒到数十秒,不是冷启动。

选 CP,不是 AP。 NameNode HA 走 CP:选举要 ZooKeeper 多数、写 edit 要 JournalNode 多数、旧 active 必须先 fence。分区时少数一侧停摆,不写分叉账本。

WAL 重放,再精确一点。 重放早在接管前就持续跑着,但光重放不够:完整保证是 quorum 写加 ZooKeeper 锁加 epoch fencing。standby 的账本是 active 账本在更早时刻的前缀,只差时间、不差内容与顺序,所以接管只需补剩余后缀,无需合并。持续重放买的是恢复速度,正确性来自它是同一条有序日志。

复盘。 这一节的全部机制可以收成三句话:身份靠锁定,数据靠日志,旧写者靠 fencing 废掉。锁丢了不能写、日志没多数不能提交、epoch 旧了写不进,三道关卡任何一道没过,脑裂就发生不了。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →