bigywater.tech
Kafka 内部原理 · 08

Leader 和 Follower:副本是怎么同步的

Follower 不是被动等 Leader 推送数据,而是主动向 Leader 发 Fetch 请求拉取。这次杀掉一个 Follower 所在的 Broker,在它缺席期间写入数据,再重启它,计时它追上 Leader、重新进入 ISR 需要多久。

验证:Follower 缺席期间被踢出 ISR,数据只写在剩下的副本上
杀掉 broker1 后,ISR 立刻从 1,2,3 变成 2,3
验证:Follower 重启后追赶数据,用计时测出重新进 ISR 花了多久
轮询发现,broker1 从进程重启到重新出现在 ISR 里,一共花了 15.37 秒

验证:Follower 缺席期间被踢出 ISR,数据只写在剩下的副本上

命令 · shell $ kill -9 <broker1-pid> # broker1 是这个分区的 Follower,不是 Leader $ kafka-topics.sh --bootstrap-server 127.0.0.1:19292,127.0.0.1:19392 --describe --topic replica-sync-demo
真实实测 · 本机执行输出
Partition: 0  Leader: 2  Replicas: 2,3,1  Isr: 2,3

杀掉 broker1 后,ISR 立刻从 1,2,3 变成 2,3——broker1 被移出了"可以参与确认写入"的副本集合,即使它在 Replicas 列表里仍然存在(说明它理论上还是这个分区的副本,只是当前不同步)。这期间写入的 5000 行数据只被复制到了 broker2(Leader)和 broker3(还在线的 Follower)。

验证:Follower 重启后追赶数据,用计时测出重新进 ISR 花了多久

命令 · shell $ kafka.Kafka node1.properties & # 重启 broker1 # 每 0.5 秒轮询一次 ISR,直到 broker1 重新出现
真实实测 · 本机执行输出
t+15.37s ISR=1,2,3
broker1 重新加入 ISR

轮询发现,broker1 从进程重启到重新出现在 ISR 里,一共花了 15.37 秒——这包含了 JVM 启动、加载数据、和向 Leader 发起 Fetch 请求、拉取缺失数据(这次缺失了 5000 行)直到追平的完整过程。重新加入 ISR 之后,查三个 Broker 上这个分区的 Log End Offset,三边都是 10000,完全一致——不是"大概同步了",是核对过 offset 数字相等。

结论

副本同步的机制是"拉"不是"推":Follower 主动向 Leader 发 Fetch 请求,跟不上时会被移出 ISR(不再参与 acks=all 的确认),重新跟上后再被加回来。这次测出的 15.37 秒不是一个通用数字(取决于缺失了多少数据、JVM 启动开销),但这个"掉线 → 移出 ISR → 拉取追赶 → 数据对齐 → 重新加入 ISR"的完整链路是走了一遍、用 offset 核对过一致性的,不是描述性的说法。

分步演示:Follower 掉线又重新追上,ISR 是怎么变化的

每一步都是本文上两节kill -9、写入数据、轮询ISR和核对offset得到的原始结果。

一个Follower从掉线到追平数据、重新进ISR,完整走一遍
这一步新验证的状态

    参考与说明

    • 本文数据来自的 3 节点 Kafka 4.3.1 集群, kill -9 broker1(这个分区的 Follower)、写入 5000 行、重启并轮询 ISR 状态;最终一致性用 kafka-get-offsets.sh 在三个 Broker 上核对,不是假设。
    • 和《Topic、Partition、Replica 到底是什么关系》一起看:那篇验证了杀 Leader 时的故障切换范围,这篇验证的是杀 Follower 时的同步/追赶机制——一个是"故障域"视角,一个是"数据同步"视角。
    • 没有涉及:本文用的是单分区小数据量(5000 行)来保证测试能在合理时间内完成,没有测试大规模数据(比如几十 GB)下 Follower 追赶所需的时间量级,这个留给容量规划相关的文章。
    京ICP备2026067502号-1京公网安备11010502063487号