bigywater.tech
ZooKeeper · 40

Proposal、事务日志与 Commit

ZAB 的广播阶段是怎么让一个写入「在整个集群里生效」的?这篇用 mntr 的计数器验证 Proposal → Ack → Commit 的对应关系,再暂停 follower 看没有多数派确认时写入处于什么状态。

500 / 500 / 500
每个 follower 收到的 proposal、commit 数,与 leader 提交数
ack 不齐就不可见
两个 follower 暂停时,leader 上的写卡住且读不到
恢复后 4 ms
多数派 ack 之后很快提交并对外可见

计数器对得上

实测 · 本机执行输出
  z40-1 follower  收到 proposal +500  收到 commit +500  本地提交 commit_count +0
  z40-2 leader    收到 proposal +0    收到 commit +0    本地提交 commit_count +500
  z40-3 follower  收到 proposal +500  收到 commit +500  本地提交 commit_count +0
  leader 的 avg_quorum_ack_latency = 5.3733 ms;avg_proposal_latency = 0.0 ms

字段名对应:zk_learner_proposal_received_count、zk_learner_commit_received_count 是 follower 收到的数量,zk_commit_count 是本节点提交到内存树的数量。

没有多数派确认

实测 · 本机执行输出
  两个 follower 暂停 1.5 秒后:异步 create 完成了吗? False | 另一个客户端读 /pending: None | leader 的 zk_outstanding_requests = 1
  follower 恢复后 4 ms:create 结果 True,/pending 现在可见: True

leader 自己也要把提案写进事务日志并 ack,所以 3 个节点里 leader 加任意一个 follower 的 ack 就够多数派。这里两个 follower 都被暂停,leader 自己的 ack 不够,提案就卡住。

分步演示

Proposal 与 Commit
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / ZooKeeper 3.9.6(brew 安装的 jar,用 java -cp 直接启动自己的实例,OpenJDK 17 Temurin,JVM 堆 128 MiB)/ 本机回环端口 7300+,数据在临时目录;默认 tickTime=2000;计时用 time.perf_counter,数字每次运行会有小幅抖动。3 节点伪集群,写入通过一个 follower 的客户端;暂停用 SIGSTOP;计数器取自 mntr
    • 没有验证:提案在 follower 上被写入事务日志的确切时刻(没有逐条追踪)、proposal 的批量化、慢 follower 对提交的影响、follower 在 ack 之后崩溃的情形
    京ICP备2026067502号-1京公网安备11010502063487号