cswork / 系统设计

第 33 章 / 共 57 章 · 约 4 分钟

关键技术:ZooKeeper 与 etcd

本页目录
  1. 1. 核心模型
  2. 2. 什么时候用
  3. 3. 什么时候不适合
  4. 4. 关键限制和数量级
  5. 5. 面试中常被追问的点
  6. 6. 和其他技术的取舍
  7. 7. 本站案例
  8. 章节测验

ZooKeeper 和 etcd 是给“协调”用的强一致存储:数据量很小,但每次写入都经过共识协议,所有节点对顺序达成一致。选主、分布式锁、服务发现、集群配置这类“全集群必须对同一件事看法一致”的问题,最后往往落到它们身上。它们不是用来存业务数据的。

1. 核心模型

  • 共识集群:通常 3 或 5 个节点,ZooKeeper 用 ZAB 协议,etcd 用 Raft。写入由 leader 发起,多数派(quorum)确认后才算提交。5 个节点能容忍 2 个故障,3 个节点能容忍 1 个;节点数取奇数,因为 4 个节点和 3 个节点一样只能容忍 1 个故障。
  • 数据模型:ZooKeeper 是树形的 znode;etcd 是扁平的键值,按前缀查询。
  • 会话和租约:客户端和集群保持心跳。ZooKeeper 的临时节点(ephemeral znode)、etcd 绑定租约(lease)的 key,在客户端失联、会话或租约过期后自动删除,这是“检测某个进程是否还活着”的基础。
  • 顺序号:ZooKeeper 的顺序节点、etcd 每次修改的全局 revision 单调递增,可以用来排队和做 fencing token。
  • 监听(watch):客户端订阅某个 key 或前缀,变化时收到通知,用于配置下发和服务列表更新。
  • 读一致性:etcd 默认线性一致读(经过 leader 确认);ZooKeeper 的读由客户端连接的那台服务器直接返回,可能略旧,需要最新值时先调用 sync。

2. 什么时候用

  • 选主:一组调度器里只能有一个在工作。大家去抢同一个带租约的 key,抢到的是 leader;leader 挂掉,租约过期,其他节点接管。
  • 分布式锁:需要真正互斥、并且能配合 fencing token 的场景。
  • 服务发现和配置:实例启动时注册临时节点,下线自动消失;配置变更通过 watch 实时推送。
  • 集群元数据:Kubernetes 把整个集群状态存在 etcd;HBase 等系统用 ZooKeeper 做协调;早期 Kafka 也依赖 ZooKeeper,新版本改用内置的 KRaft。
  • 小规模的唯一编号分配:一次分配一段号段,例如短链接服务里每台服务器从 ZooKeeper 租一段 ID。

3. 什么时候不适合

  • 业务数据和大对象:容量小,值也小,写入吞吐有限。
  • 高频写入:每次写都要过共识,每秒几千到几万次写就是上限量级,计数器、会话这类数据交给 Redis 或数据库。
  • 跨地域低延迟写入:共识要多数派确认,节点跨洋分布时每次写都要跨洋往返。
  • 只需要“尽量互斥”的场景:例如避免重复发同一封提醒邮件,Redis 锁或数据库唯一约束更简单。

4. 关键限制和数量级

维度要记住的事
集群大小3 或 5 个节点;节点越多写入越慢,因为要等更多确认
数据量etcd 默认存储配额 2 GB,建议不超过 8 GB;单个请求约 1.5 MB;ZooKeeper 单个 znode 默认上限约 1 MB
写入吞吐每秒几千到几万次,受磁盘 fsync 和网络往返限制
可用性失去多数派就不能写入:这是 CAP 里选择一致性的代价
故障检测会话 / 租约超时通常是几秒到几十秒,决定了故障切换的速度

5. 面试中常被追问的点

  • 为什么节点数是奇数? 多数派是 ⌊n/2⌋+1。4 个节点要 3 个确认,只能容忍 1 个故障,和 3 个节点一样,却多一台机器、写入更慢。
  • 旧 leader 以为自己还是 leader 怎么办(脑裂)? 旧 leader 可能因为 GC 暂停或网络分区,没察觉自己的租约已经过期。解决办法是 fencing token:每次选主得到一个更大的编号(etcd revision、ZooKeeper zxid),下游存储只接受编号不小于已见过最大值的写入。
  • 用 ZooKeeper 实现锁的经典做法? 每个客户端在锁目录下创建临时顺序节点,编号最小的持有锁;其他客户端只 watch 比自己小一号的节点,避免锁释放时所有人一起被唤醒(羊群效应)。
  • 集群一半节点挂了会怎样? 剩下的不够多数派,集群拒绝写入,依赖它的选主、锁都停摆。所以要把节点放在不同机架或可用区。
  • 为什么不用 Redis 做选主? Redis 主从是异步复制,故障切换可能让两个客户端同时持锁;对正确性要求高的选主,要用共识系统或者加 fencing。

6. 和其他技术的取舍

选项适合代价
etcdKubernetes 生态、gRPC 接口、线性一致读数据量小
ZooKeeperHadoop / HBase 等老牌生态、成熟的锁和队列配方运维 JVM 集群;默认读可能略旧
Consul服务发现加健康检查、多数据中心另一个要运维的系统
Redis 锁简单、快,容忍偶发的重复执行故障切换时不保证互斥
数据库行锁 / advisory lock已有数据库、低频协调依赖数据库可用性,持锁时间不宜长

7. 本站案例

章节测验

5 道选择题
  1. 一个 etcd 集群有 5 个节点,最多同时挂掉几个仍然能够继续写入?
  2. 旧 leader 因为长时间 GC 暂停,租约已经过期、新 leader 已经产生,它恢复后还在往存储写数据。怎么防止它写坏数据?
  3. 下面哪个用途最适合放在 etcd 或 ZooKeeper 上?
  4. 用 ZooKeeper 实现锁时,为什么每个等待者只 watch 比自己小一号的节点,而不是 watch 锁目录?
  5. 为什么协调集群一般用 3 或 5 个节点,而不是 4 或 6 个?

章节讨论

正在加载讨论…