跳到内容

第 7 卷 / 第 3 章 / 第 2 课

3.2 Raft 的安全读取、成员变更与快照 ​

日志能安全复制,不代表所有读都线性一致,也不代表可以直接改配置文件增删节点。生产 Raft 系统还要处理 Leader 过期、成员集合切换、日志压缩、磁盘和延迟。

Leader 本地读可能过期 ​

一个旧 Leader 被网络隔离后,可能暂时不知道新 Leader 已在更高 term 当选。若它直接读取本地状态并响应,会返回过期结果。

线性一致读常见方案:

  • 读请求先通过日志提交,安全但增加写路径开销;
  • ReadIndex/多数派确认:Leader 先确认本 term 至少已有一条日志提交,再为这次读记录安全 index,并通过读请求到达后的多数派通信确认领导权,最后等待状态机应用到该 index;
  • lease read:依赖时钟和租约假设,必须严格满足实现条件。

刚当选的 Leader 常先提交本 term 的 no-op,以确定已提交前缀。旧心跳的成功响应不能当作一次新读的领导权证明。否则旧 Leader 在隔离前的“健康证明”,会被拿来担保隔离后的旧数据。

Follower read 通常是陈旧读,除非先为这次读从 Leader 获取安全 read index 并追赶。API 应明确读级别,不能把任意节点 GET 宣称强一致。

Leader 完整性不等于 Leader 永不返回旧数据 ​

Raft 的日志安全保证已提交条目不会被不同值覆盖。客户端可见的线性一致性还需要:

  • 若一次逻辑命令会重试,复用唯一请求 ID,并把已执行 ID 及结果纳入复制状态和快照,Leader 切换后仍能去重;仅生成 ID 不会阻止重复应用;
  • 读使用安全协议;
  • 状态机按提交顺序且确定性执行;
  • 快照与日志恢复保持相同状态;
  • 客户端不绕过上述读取和写入协议直接访问副本状态。

成员变更不能直接替换名单 ​

从旧配置 {A,B,C} 直接切到新配置 {C,D,E},旧集合的 {A,B} 与新集合的 {D,E} 都是各自的多数派,却没有共同成员,可能分别选出 Leader。

Raft 论文使用 joint consensus:过渡阶段的决策同时需要旧配置和新配置的多数派,之后再提交纯新配置。具体系统也可能使用一次增删一个成员等变体,但必须遵守其协议和操作顺序。

例如三投票成员中已有一个故障,再直接新增一个尚未启动的投票成员,分母会从 3 变成 4,多数门槛由 2 变成 3,而健康成员仍只有 2 个。etcd 支持先加入非投票 learner,追赶后再提升,并会检查不安全的成员变更。修复时要按产品支持的顺序处理故障成员,不能靠多填一个地址“凑人数”。

成员变更前检查:

  • 新节点是否追赶到足够接近日志尾部;
  • 当前是否已有故障成员;
  • 变更期间还剩多少 quorum 余量;
  • 节点身份是否稳定,旧数据目录会不会以错误身份加入;
  • 自动化是否会并发执行多个配置变更。

Snapshot 压缩已提交前缀 ​

日志无限增长会拖慢重放并占满磁盘。节点可以把某个已应用位置的状态及对应 index/term 保存为快照,确认快照持久化后再删除覆盖的日志;这里的 index 是快照最后包含的条目,不一定是当前日志末尾。

落后 Follower 若所需日志已被压缩,Leader 通过 InstallSnapshot 发送快照,再从快照之后继续复制。

快照必须:

  • 与某个已应用 index 原子对应;
  • 包含恢复状态机需要的数据、去重状态和对应位置的成员配置;
  • 校验完整性并安全替换旧状态;
  • 与并发日志追加协调;
  • 在崩溃中不会留下“日志已删、快照未完成”的空洞。

用于日志压缩的内部快照,会随集群状态前进,也可能与原磁盘一起损坏,不能单独承担误删除恢复和历史保留。独立导出、异地保留并验证可恢复的快照可以成为备份,例如 etcd 的 snapshot backup;备份是否有效,还取决于保留策略、版本兼容和恢复演练。

磁盘是共识路径的一部分 ​

多数派网络很快但磁盘 fsync 很慢,提交仍会慢。磁盘写满、尾延迟、固件问题和文件系统损坏都可能使节点失去参与 quorum 的能力。

需要观察:

  • Leader 变更和无 Leader 时长;
  • propose 到 commit、commit 到 apply 的延迟;
  • 每个 Follower 的 matchIndex 落后量;
  • fsync 延迟、磁盘空间和 WAL 错误;
  • 快照生成、传输和恢复耗时;
  • 成员变更审计和失败状态。

etcd 等系统的使用边界 ​

etcd 面向较小、强一致的控制面状态,不适合直接装载大对象和高吞吐数据流。这个产品边界不能推广成 Raft 算法的用途限制:分片数据库也可以用多个 Raft group 复制业务数据。使用 etcd 时仍要遵守其请求大小、存储容量、压缩、watch 和备份限制。

客户端还应使用产品提供的事务、revision 和 watch 语义,而不是从“底层是 Raft”自行推导 API 保证。

与 Paxos、ZAB 的关系 ​

Paxos 家族和 Raft 都利用多数派交集实现共识,但抽象和工程组织不同;不能把 Raft 的角色、日志规则逐项套在所有 Paxos 实现上。

ZooKeeper 的 ZAB 面向主节点驱动的原子广播和恢复阶段,ZooKeeper 客户端语义还包含会话、watch 和顺序保证。学习一种协议能建立基础,但产品行为必须回到自身规范。

故障测试 ​

  • Leader 在本地追加后、复制前崩溃;
  • 多数派复制后、客户端收到响应前崩溃;
  • 旧 Leader 被隔离,新 Leader 当选后旧 Leader 恢复;
  • Follower 日志有冲突后缀;
  • 快照写到一半进程终止;
  • 成员变更中途失去一个节点;
  • 磁盘写满或 fsync 长尾。

共识实现的可信度来自模型检查、成熟实现和系统性故障测试,不来自一段简化选举代码。

参考资料 ​

Built with VitePress | Software Systems Atlas