2026-10-31·por 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 旧了写不进,三道关卡任何一道没过,脑裂就发生不了。

sobre esta entrada

Una de las entradas de wiki de sijie. La IA en este sitio está anclada en el mismo corpus y responde en la voz de sijie, con citas que vuelven a entradas como esta — responder le cuesta dinero a sijie, así que espera detrás de un código: ingrese código de acceso →