乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • ZooKeeper 学习路径
  • 第1章 分布式协调与ZooKeeper概述

    • ZooKeeper 概述
  • 第2章 单机与集群搭建

    • 配置参数详解
    • 集群搭建
  • 第3章 数据模型与ZNode

    • ZNode 详解
    • 节点类型对比
    • 顺序节点
    • ACL 权限控制
  • 第4章 会话与Watcher机制

    • 会话机制
    • Watcher 机制
  • 第5章 ZAB协议与一致性保证

    • ZAB 协议
    • 一致性保证
    • 数据同步
  • 第6章 Leader选举

    • Leader 选举
  • 第7章 典型应用:分布式锁

    • 分布式锁
  • 第8章 典型应用:配置中心与命名服务

    • 配置中心
    • 命名服务
  • 第9章 客户端编程基础(Java原生API)

    • Java 原生 API 编程
  • 第10章 运维与监控

    • 四字命令
    • 监控体系
  • 第11章 面试考点与设计思想

    • 设计思想
    • 面试考点汇编

本章定位:从客户端角度理解 ZooKeeper 到底提供什么样的一致性语义,以及什么时候可能出现"读到旧数据"。

定义与作用

ZooKeeper 官方声称的一致性保证包含三条:

保证含义
顺序一致性(Sequential Consistency)来自同一客户端的更新按发送顺序执行
原子性(Atomicity)更新要么在所有节点成功,要么全部失败
单一系统映像(Single System Image)客户端看到的是同样的系统状态视图(无论连到哪个 Server)

此外还有两个隐含保证:

保证含义
持久性(Durability)已确认的更新不会丢失,即使节点故障
及时性(Timeliness)系统状态在一定时间窗口内保证新鲜(通过 ZAB 同步)

核心原理

一致性模型图解

ZooKeeper 默认不保证所有 Server 的视图实时一致。Leader 写入成功时,Follower 可能尚有延迟。调用 sync() 可强制 Follower 同步到最新状态。

为什么"不保证读到最新"

ZooKeeper 采用主备异步复制(Leader 同步写入,Follower 异步应用)。读请求可以直接由 Follower 处理,提升了读吞吐量,代价是可能读到滞后数据。

对于需要"读你所写"的场景,应连接到 Leader(通过 zkCli.sh -server 显式指定)或先调用 sync()。

完整示例

示例一:Follower 滞后读验证

场景说明:Leader 写入后立即在 Follower 读取,观察滞后现象。

# Step 1:确定连接的是 Follower
zkCli.sh -server 127.0.0.1:2181
[zkshell: 0] stat /
# 输出中如果 Mode: follower,说明连接的是 Follower

# Step 2:在 Leader 上快速写入
# (另一个终端连 2182,已知为 Leader)
zkCli.sh -server 127.0.0.1:2182
[zkshell: 0] create /consistency-test "initial"
Created /consistency-test
# Step 3:立即在 Follower 读取
# (回到 2181 终端)
[zkshell: 1] get /consistency-test
initial
# 可能成功(同步快)或 NoNode(同步慢)

多数情况下数据几乎立即可读,因为 ZAB 的广播延迟极小。但在极端负载或网络抖动时可观察到微秒到毫秒级的滞后。

示例二:sync() 强制同步

场景说明:使用 sync 命令确保 Follower 读到最新。

# Follower 终端
[zkshell: 0] create /sync-test "before-sync"
Created /sync-test

# Leader 修改
# (另一个终端)
[zkshell: 0] set /sync-test "after-sync"

# Follower 立即 sync 后读取
[zkshell: 1] sync /sync-test
Sync is OK
[zkshell: 2] get /sync-test
after-sync

Java API 实现:

// 同步并读取最新数据
zk.sync("/key", null, null);
byte[] data = zk.getData("/key", false, null);

操作前后对比:

操作get /sync-test说明
Leader set "after-sync"Follower 可能读到 "before-sync"同步延迟
sync + getFollower 读到 "after-sync"强制同步

示例三:持久性验证

场景说明:写入后立即 Kill Leader,验证数据不丢失。

# Leader 终端
[zkshell: 0] create /durable-test "persistent-data"
Created /durable-test
# 立即 Kill Leader 进程
kill -9 <leader-pid>

# 新 Leader 选举完成后(约 10s)
zkCli.sh -server 127.0.0.1:2181   # 新 Leader
[zkshell: 0] get /durable-test
persistent-data
# 数据不丢失!

操作前后对比:

事件集群状态/durable-test
写入成功正常运行存在
Leader 宕机选举中—
新 Leader 就绪恢复正常存在,数据未丢失

易错场景与面试考点

易错场景

1. 认为 ZooKeeper 是强一致性系统

ZooKeeper 提供的是顺序一致性(Sequential Consistency),不是线性一致性(Linearizability)。区别:

  • 顺序一致性:同一客户端的操作按顺序执行,但不同客户端的操作可以被其他客户端看到任何交错顺序
  • 线性一致性:所有操作看起来在某个全局时间点上原子地执行

ZooKeeper 的写操作(经过 Leader)满足线性一致性,但读操作(尤其是从 Follower 读)不满足。

2. 读-修改-写 竞态条件

// 错误示范
byte[] data = zk.getData("/counter", false, null);
int counter = Integer.parseInt(new String(data));
counter++;
zk.setData("/counter", Integer.toString(counter).getBytes(), -1);
// 两个客户端可能读到相同的 counter 值

正确做法:使用版本号乐观锁(setData(..., version))。

3. 客户端重连到不同 Server 的视图不一致

客户端从 Server A 断开重连到 Server B 时,如果 B 的状态滞后于 A,客户端可能看到"倒退"的数据。解决方法:重连后先 sync()。

面试高频题

Q:ZooKeeper 是 CP 还是 AP?

A:CP(一致性 + 分区容错)。在网络分区发生时,ZooKeeper 宁可牺牲可用性(少数派停止服务)也要保证数据一致性。这与 Eureka(AP)形成对比。

Q:什么场景下 ZooKeeper 可能读到过时数据?

A:1)客户端连接 Follower 且 Follower 尚未同步到最新;2)客户端重连到新 Server 且新 Server 状态滞后;3)Snapshot 恢复期间(服务端使用快照 + 事务日志重建状态时)。解决方法:关键读操作前调用 sync() 或始终连接 Leader。

Q:ZooKeeper 能用于需要严格线性一致性的场景吗?

A:可以通过以下方式近似实现:

  1. 所有读操作前先调用 sync()
  2. 所有读写都经过 Leader(客户端始终连接 Leader 地址)
  3. 使用版本号乐观锁保证写操作的安全条件

小结

要点说明
顺序一致性同一客户端的操作有序执行
原子性更新 Quorum 确认后全部提交
单一系统映像客户端看到一致的系统状态
Follower 读可能滞后,使用 sync() 保证新鲜
CP 系统分区时牺牲可用性、保证一致性
乐观锁通过 version 避免竞态条件

理解 ZooKeeper 的一致性边界才能在生产环境中正确使用。下一节深入数据同步机制,揭示 ZAB 协议的具体实现细节。

上一页
ZAB 协议
下一页
数据同步