Kafka Leader 和 Follower 深度解析:副本协同的奥秘与数据一致性保障
Kafka Leader 和 Follower 深度解析:副本协同的奥秘与数据一致性保障
-
- 前言
- 一、Leader 和 Follower 的基本概念
-
- 1.1 从分区视角看副本
- 1.2 Leader 副本:分区的主节点
- 1.3 Follower 副本:分区的从节点
- 二、Leader 和 Follower 的协同工作机制
-
- 2.1 核心协同流程概览
- 2.2 关键概念:LEO 和 HW
- 2.3 详细协同步骤解析
-
- 步骤1:生产者写入消息
- 步骤2:Follower 拉取消息
- 步骤3:Leader 更新 RemoteLEO 和 HW
- 步骤4:Follower 更新自己的 HW
- 2.4 HW 更新的“两轮 Fetch”机制
- 三、Leader 选举与故障转移
-
- 3.1 触发选举的场景
- 3.2 ISR:决定谁有资格成为 Leader
- 3.3 Leader 选举流程
- 3.4 Unclean 领导者选举:权衡一致性与可用性
- 四、深入数据同步机制
-
- 4.1 同步复制 vs 异步复制
- 4.2 副本同步的流量控制
- 4.3 Leader 和 Follower 的状态机
- 五、实际案例与最佳实践
-
- 5.1 查看副本状态
- 5.2 生产配置建议
- 5.3 故障场景模拟
- 六、常见问题与排查
-
- 6.1 Under-replicated 分区
- 6.2 Leader 选举频繁
- 6.3 数据不一致问题
- 总结
|
🌺The Begin🌺点点关注,收藏不迷路🌺
|
前言
在 Kafka 的分布式架构中,副本机制是实现高可用和数据持久化的基石。而副本机制的核心,就是 Leader(领导者) 和 Follower(追随者) 这两个角色的精妙分工与协作。它们之间的关系就像一支交响乐队的指挥和乐手——指挥负责统筹全局,乐手则严格按照指挥的指令演奏,共同奏响数据的华美乐章。
本文将深入剖析 Kafka 中 Leader 和 Follower 的定义、职责,并通过详细的流程图和源码级别的原理解析,揭示它们在副本同步、故障转移等场景中如何协同工作,确保 Kafka 集群的高可靠性和高性能。
一、Leader 和 Follower 的基本概念
1.1 从分区视角看副本
在 Kafka 中,每个主题(Topic)可以划分为多个分区(Partition),而每个分区又可以配置多个副本(Replica)来提供数据冗余。这些副本分布在不同的 Broker 节点上,共同构成了分区的数据备份集,称为 AR(Assigned Replicas)。
在 AR 集合中,副本被划分为两种角色:
Topic: orders (分区0)
Leader 副本
Broker 1
Producer
Consumer
Follower 副本
Broker 2
Follower 副本
Broker 3
1.2 Leader 副本:分区的主节点
Leader 副本是每个分区的“主副本”,在创建分区时由 Kafka 选举产生。它的核心特征包括:
- 唯一性:每个分区有且仅有一个 Leader 副本
- 全权负责:处理所有客户端的读写请求(生产者和消费者都只能与 Leader 交互)
- 协调同步:负责管理 Follower 副本的数据同步进度
1.3 Follower 副本:分区的从节点
Follower 副本是 Leader 的“追随者”,可以有多个。它们的主要特点:
- 不对外服务:Follower 不处理任何客户端请求
- 异步同步:持续从 Leader 拉取消息,保持数据同步
- 冷备角色:仅作为数据备份,在 Leader 故障时参与选举
设计哲学:Kafka 选择让所有读写请求都经过 Leader,虽然牺牲了读操作的扩展性,但换来了实现简单、数据一致性好(避免读取到未同步的过期数据)等优势。
二、Leader 和 Follower 的协同工作机制
2.1 核心协同流程概览
Leader 和 Follower 的协同工作可以概括为以下流程:
Consumer
Follower2
Follower1
Leader
Producer
Consumer
Follower2
Follower1
Leader
Producer
阶段1:消息写入
阶段2:Follower拉取
阶段3:更新HW
阶段4:消息可消费
发送消息
写入本地Log,更新LEO
Fetch请求 (offset=当前LEO)
Fetch请求 (offset=当前LEO)
返回消息数据
返回消息数据
写入本地Log,更新LEO
写入本地Log,更新LEO
下一轮Fetch(携带最新LEO)
更新RemoteLEO,计算新的HW
下一轮Fetch(携带最新LEO)
Fetch消息
返回HW之前的消息
2.2 关键概念:LEO 和 HW
在理解协同机制前,必须先掌握两个核心指标:
| 概念 | 全称 | 定义 | 维护者 |
|---|---|---|---|
| LEO | Log End Offset | 日志末端位移,指向下一条待写入消息的位置 | 每个副本独立维护 |
| HW | High Watermark | 高水位,消费者能看到的最后一条消息的位移 | Leader 统一维护 |
| RemoteLEO | – | Leader 维护的每个 Follower 的 LEO 副本 | 仅 Leader 维护 |
2.3 详细协同步骤解析
步骤1:生产者写入消息
当生产者向分区发送消息时(以 acks=all 为例):
- 生产者找到分区 Leader,发送消息
- Leader 将消息写入本地日志,Leader LEO 增加 1
- Leader HW 暂不更新(等待 Follower 确认)
步骤2:Follower 拉取消息
Follower 持续向 Leader 发送 Fetch 请求进行数据同步:
- 每个 Follower 在请求中携带自己的当前 LEO
- Leader 收到请求后,返回从该 LEO 开始的消息数据
- Follower 将消息写入本地日志,更新自己的 Follower LEO
步骤3:Leader 更新 RemoteLEO 和 HW
Leader 在收到 Follower 的 Fetch 请求时,会执行:
- 更新该 Follower 对应的 RemoteLEO 为请求中携带的 LEO
- 重新计算 HW:取 所有 RemoteLEO 的最小值 和 Leader LEO 的较小值
- 将新的 HW 通过下一次 Fetch 响应返回给 Follower
步骤4:Follower 更新自己的 HW
Follower 在收到 Fetch 响应时:
- 比较自己的 LEO 和响应中携带的 HW
- 取较小值更新自己的 Follower HW
2.4 HW 更新的“两轮 Fetch”机制
这是一个非常精妙的细节:HW 的更新需要两轮 Fetch 请求才能完成。
示例场景:初始状态(所有副本 LEO=0,HW=0),生产者写入一条消息。
| 阶段 | 操作 | Leader LEO | RemoteLEO(F1) | Leader HW | Follower1 LEO | Follower1 HW |
|---|---|---|---|---|---|---|
| 初始 | – | 0 | 0 | 0 | 0 | 0 |
| 1 | 生产者写入消息 | 1 | 0 | 0 | 0 | 0 |
| 2 | F1 第一次 Fetch(请求 offset=0) | 1 | 1(从请求中获取) | min(1,1)=1 | 1 | 0(响应 HW=0) |
| 3 | F1 第二次 Fetch(请求 offset=1) | 1 | 1 | 1 | 1 | min(1,1)=1 |
关键发现:Follower 的 HW 在第二轮 Fetch 时才更新为 1。这意味着即使 Follower 已经同步了消息,也需要等待下一轮 Fetch 才能知道自己“同步完成”。
三、Leader 选举与故障转移
3.1 触发选举的场景
当 Leader 副本所在的 Broker 发生故障(宕机、网络分区等)时,Kafka 必须选举新的 Leader 以保证服务可用。
3.2 ISR:决定谁有资格成为 Leader
ISR(In-Sync Replicas) 是 Kafka 动态维护的一个同步副本集合,包含所有与 Leader“足够同步”的副本(包括 Leader 自身)。
同步判断标准:Follower 在 replica.lag.time.max.ms(默认 10 秒)内持续向 Leader 发送 Fetch 请求且未落后太多。
AR (所有副本)
ISR:同步副本集合
Leader + 同步的Follower
OSR:滞后副本集合
不同步的Follower
AR
3.3 Leader 选举流程
当 Leader 故障时,Kafka 的 Controller 组件负责执行选举:
- 检测故障:Controller 通过 ZooKeeper 监听或内部心跳发现 Leader 所在的 Broker 下线
- 获取 ISR:从元数据中获取该分区的当前 ISR 列表
-
选举新 Leader:
- 如果 ISR 非空,从 ISR 中选出一个副本作为新 Leader(通常选择第一个)
- 如果 ISR 为空,根据
unclean.leader.election.enable配置决定是否允许从 OSR(非同步副本)中选举
- 通知更新:Controller 将新 Leader 信息通知所有 Broker
// 选举算法伪代码
public int electLeaderForPartition(Partition partition) {
List<Integer> isr = partition.getIsr();
if (!isr.isEmpty()) {
// 优先从 ISR 中选举(保证数据不丢失)
return isr.get(0); // 通常选第一个
} else if (config.isUncleanLeaderElectionEnabled()) {
// 允许非同步副本成为 Leader(可能丢数据,换可用性)
return partition.getAnyReplica();
} else {
// 无法选举,分区不可用
throw new NoLeaderException();
}
}
3.4 Unclean 领导者选举:权衡一致性与可用性
参数 unclean.leader.election.enable 决定了是否允许非 ISR 副本成为 Leader:
| 配置值 | 行为 | 数据一致性 | 可用性 | 适用场景 |
|---|---|---|---|---|
| false(默认) | 只有 ISR 副本可当选 | ✅ 保证已提交数据不丢失 | ❌ 可能降低可用性 | 大多数生产环境 |
| true | 允许 OSR 副本当选 | ❌ 可能丢失已提交数据 | ✅ 保持可用性 | 牺牲一致性换可用性的极端场景 |
建议:生产环境保持默认值
false,数据一致性比短时不可用更重要。
四、深入数据同步机制
4.1 同步复制 vs 异步复制
根据生产者的 acks 参数,Leader 处理消息确认的方式不同:
acks=0(发送即成功)
生产者发送
立即返回成功
可能写入失败
acks=1(Leader确认)
生产者发送
Leader写入
立即返回成功
后台异步同步给Follower
acks=all(同步复制)
生产者发送
Leader写入
等待所有ISR Follower确认
返回成功
| acks 取值 | 数据可靠性 | 性能 | 适用场景 |
|---|---|---|---|
| 0 | 最低(可能丢消息) | 最高 | 日志、监控等可丢数据场景 |
| 1 | 中等(Leader 确认即成功) | 高 | 大部分业务场景 |
| all/-1 | 最高(ISR 全部确认) | 较低 | 金融、交易等关键数据 |
4.2 副本同步的流量控制
Kafka 设计了精妙的限流机制避免 Follower 同步拖垮 Leader:
- 延迟确认:Follower 接收到数据后等待一段时间(通常 100ms)再确认
- 动态流控:Leader 根据每个 Follower 的 ACK 速率动态调整发送数据量
- Purgatory 暂存:当 Follower 暂时无法接收时,请求会被暂存到 Purgatory 中等待
4.3 Leader 和 Follower 的状态机
每个副本内部都维护着一个状态机,管理角色转换:
Follower:
副本创建
同步中
ISR:
ISR
同步中:
追赶上Leader
落后超时
Leader:
被选举为新Leader
Leader
收到新的Leader选举通知
:
Broker宕机
五、实际案例与最佳实践
5.1 查看副本状态
使用 Kafka 命令行工具查看副本分布和 ISR 状态:
# 查看 topic 详情(包括副本分布)
kafka-topics.sh --describe --topic my-topic --bootstrap-server localhost:9092
# 输出示例
Topic: my-topic Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3
Topic: my-topic Partition: 1 Leader: 2 Replicas: 2,3,1 Isr: 2,3,1
Topic: my-topic Partition: 2 Leader: 3 Replicas: 3,1,2 Isr: 3,1
注意观察 Isr: 3,1——这表示分区 2 的 ISR 中只有副本 3 和 1,副本 2 已落后被剔除。
5.2 生产配置建议
为保证 Leader-Follower 协同工作的高可靠性:
# Broker 端配置
replication.factor=3 # 副本数
min.insync.replicas=2 # 最小 ISR 副本数
unclean.leader.election.enable=false # 禁止非 ISR 选举
replica.lag.time.max.ms=30000 # Follower 同步超时
# Producer 端配置
acks=all # 等待所有 ISR 确认
enable.idempotence=true # 开启幂等性
max.in.flight.requests.per.connection=5 # 配合幂等性
5.3 故障场景模拟
场景:分区 0 的 Leader 在 Broker 1,Follower 在 Broker 2、3。
- 正常情况:所有读写经过 Broker 1,Follower 2、3 持续同步
-
Broker 1 宕机:
- Controller 检测到 Broker 1 下线
- 查看分区 0 的 ISR = [1,2,3],移除故障的 1 后为 [2,3]
- 从 [2,3] 中选举新 Leader(假设选 2)
- 更新元数据,通知所有 Broker
- 恢复后:原 Leader(Broker 1)重启后作为 Follower 加入,从新 Leader(Broker 2)同步数据
六、常见问题与排查
6.1 Under-replicated 分区
现象:kafka-topics.sh --describe 显示分区副本数少于配置值,或 ISR 小于 AR。
可能原因:
- Follower 所在 Broker 负载过高,同步超时
- 网络延迟导致 Follower 被踢出 ISR
- Broker 磁盘空间不足
排查命令:
# 查看 under-replicated 分区
kafka-topics.sh --describe --under-replicated-partitions --bootstrap-server localhost:9092
6.2 Leader 选举频繁
现象:集群监控显示 Leader 变更次数过多。
可能原因:
- Broker 不稳定,频繁重启
- 网络分区导致心跳超时
- GC 时间过长导致 Broker 失联
优化建议:
- 调整
heartbeat.interval.ms和session.timeout.ms - 优化 JVM GC 参数
- 检查网络稳定性
6.3 数据不一致问题
Kafka 0.11 之前存在 HW 机制导致的潜在数据不一致问题(如数据丢失、Leader 切换后数据不一致)。0.11 版本引入了 Leader Epoch 机制替代 HW 作为截断依据,彻底解决了这一问题。
总结
Kafka 中的 Leader 和 Follower 通过精妙的分工协作,构建了一个高可用、高可靠的消息系统:
| 角色 | 核心职责 | 协同机制 |
|---|---|---|
| Leader | 处理所有读写请求,协调同步 | 维护 LEO、RemoteLEO、HW,响应 Fetch 请求 |
| Follower | 异步拉取数据,保持同步 | 持续发送 Fetch 请求,更新本地 LEO 和 HW |
| ISR | 维护同步副本集合 | 动态调整,决定选举资格 |
| HW/LEO | 标识数据同步进度 | 两轮 Fetch 机制更新,控制消费可见性 |
理解 Leader 和 Follower 的协同原理,不仅有助于我们正确配置 Kafka 集群,更能在故障发生时快速定位问题、采取正确的恢复策略。希望本文能帮助你深入掌握这一 Kafka 核心机制。
思考题:当生产者的 acks=all 且 min.insync.replicas=2 时,如果 ISR 中只有 Leader 一个副本(另外两个 Follower 均已落后),此时发送消息会成功还是失败?为什么?欢迎在评论区分享你的答案!

|
🌺The End🌺点点关注,收藏不迷路🌺
|