Skip to content

副本机制与高可用深度解析

面试官:说说 Kafka 的副本机制吧,ISR 是什么?

:ISR(In-Sync Replicas)是 Kafka 高可用的核心机制,表示当前与 Leader 保持同步的副本集合。Leader 处理读写,Follower 同步数据,ISR 动态调整,保证 acks=all 时数据至少在 ISR 内所有副本写入成功。

面试官:那 HW 和 LEO 又是什么?Consumer 为什么只能消费 HW 之前的消息?

这个追问是面试的高频考点。能讲清楚 HW 推进机制和 Leader Epoch 解决的数据不一致问题,才是高级候选人的标志。


Q1:什么是 ISR?副本什么时候会被踢出 ISR?必考

Section titled “Q1:什么是 ISR?副本什么时候会被踢出 ISR?”

回答要点

ISR(In-Sync Replicas) 是当前与 Leader 保持同步的副本集合(包含 Leader 本身)。

架构示意

Partition 0 的副本状态:
Leader: Broker1 ← 在 ISR 中
Follower: Broker2 ← 在 ISR 中(同步正常)
Follower: Broker3 ← 不在 ISR 中(落后太多,被踢出)
ISR = {Broker1, Broker2}

加入/离开 ISR 的条件

离开 ISR(被踢出)

# 触发条件
replica.lag.time.max.ms=30000 # 默认 30 秒
Follower 超过 30 秒没有向 Leader 发送 Fetch 请求
Follower 的 LEO 落后 Leader 的 LEO 超过阈值
→ 该副本被踢出 ISR,标记为 Out-of-Sync Replica

重新加入 ISR

被踢出的副本追上了 Leader 的 LEO(完全同步)
→ 自动重新加入 ISR

对比表格

状态是否在 ISR同步状况能否参与选举
Leader主副本
同步正常的 FollowerLEO ≈ Leader LEO
落后的 FollowerLEO < Leader LEO否(除非 unclean 选举)

本质一句话:ISR 是动态变化的,网络抖动、GC 停顿、磁盘 IO 慢都可能导致 Follower 临时离开 ISR,追上后自动恢复。


Q2:ISR 只剩 Leader 一个副本时,acks=all 还能正常写入吗?高频

Section titled “Q2:ISR 只剩 Leader 一个副本时,acks=all 还能正常写入吗?”

回答要点

取决于 min.insync.replicas 的配置:

场景对比

场景1:min.insync.replicas=1(默认)

ISR = {Leader}(只有 1 个副本)
acks=all 时:
Leader 写入成功 → 返回 ACK
→ 实际等价于 acks=1
风险:
Leader 宕机 → 数据丢失
失去了多副本保障的意义

场景2:min.insync.replicas=2(推荐生产配置)

ISR = {Leader}(只有 1 个副本)
acks=all 时:
Leader 写入成功 → 检查 ISR 副本数 < 2
→ Broker 拒绝写入,返回 NotEnoughReplicasException
优点:
宁可牺牲可用性(写入失败),也不能让数据在不安全的情况下"假成功"
Producer 收到异常后可以重试或报警,比数据静默丢失要好得多

配置推荐

# 最高可靠性(金融/支付场景)
replication.factor=3 # 总副本数 3 个
min.insync.replicas=2 # ISR 至少 2 个副本
acks=all # 等待 ISR 全部确认
unclean.leader.election.enable=false # 禁止不完整副本成为 Leader
# 监控告警
UnderReplicatedPartitions > 0 → ISR 缩水,立即告警

本质一句话min.insync.replicas 是数据安全的最后一道防线,ISR 副本数不足时拒绝写入,避免假成功。


Q3:ISR 为什么是动态变化的?频繁进出 ISR 有什么影响?中频

Section titled “Q3:ISR 为什么是动态变化的?频繁进出 ISR 有什么影响?”

回答要点

动态变化的原因

ISR 不是固定不变的,而是根据副本同步状况实时调整

正常状态:
ISR = {Broker1, Broker2, Broker3}
Broker2 网络抖动,30 秒未同步:
ISR = {Broker1, Broker3}(Broker2 被踢出)
Broker2 网络恢复,追上 Leader:
ISR = {Broker1, Broker2, Broker3}(Broker2 重新加入)

频繁进出 ISR 的影响

性能影响

ISR 缩小 → acks=all 时需要确认的副本减少 → 写入延迟降低
ISR 扩大 → acks=all 时需要确认的副本增加 → 写入延迟升高
频繁 ISR 变更 → HW 推进不稳定 → 消费可见性延迟抖动

监控指标

Terminal window
# ISR 变更频率
kafka.server:type=ReplicaManager,name=IsrShrinksPerSec # ISR 缩小速率
kafka.server:type=ReplicaManager,name=IsrExpandsPerSec # ISR 扩大速率
# 告警阈值
IsrShrinksPerSec > 1 ISR 频繁缩小,需排查网络/磁盘/GC

排查思路

现象可能原因排查方法
Follower 频繁离开 ISR网络延迟高检查网络带宽、延迟
Follower 频繁离开 ISRGC 停顿长查看 GC 日志,调整堆内存
Follower 频繁离开 ISR磁盘 IO 慢检查磁盘利用率、iowait
ISR 长期只有 Leader副本配置错误检查副本分布、跨机房延迟

Q4:HW 和 LEO 分别是什么?Consumer 为什么只能消费 HW 之前的消息?必考

Section titled “Q4:HW 和 LEO 分别是什么?Consumer 为什么只能消费 HW 之前的消息?”

回答要点

核心概念

LEO(Log End Offset):
该副本下一条待写入消息的 offset
即当前最新消息的 offset + 1
HW(High Watermark,高水位):
ISR 中所有副本中最小的 LEO
Consumer 只能消费 offset < HW 的消息(已确认被 ISR 全部写入)

时序示例

初始状态(3副本全在 ISR):
Leader: LEO=5, HW=5
Follower1: LEO=5, HW=5
Follower2: LEO=5, HW=5
Producer 写入3条消息(offset 5, 6, 7)后:
Leader: LEO=8, HW=5 ← Leader LEO 已更新,但 HW 还没推进
Follower1 同步完成:LEO=8
Follower2 同步完成:LEO=8
Leader 收到所有 ISR 副本的 LEO 汇报后:
HW = min(LEO of all ISR) = min(8, 8, 8) = 8
所有副本 HW 更新为 8
Consumer 此时可消费 offset 0~7 的消息(offset < HW=8)

ASCII 图表

Leader 的 Log 文件:
offset: 0 1 2 3 4 5 6 7 8 9 10
├───────────────┤──┤──┤
↑ ↑ ↑ ↑
HW=5 LEO=8
│ │
Consumer 可消费 已写入但未确认
(ISR 全部确认) (Follower 可能还没同步)

Consumer 只能消费 HW 之前的原因

场景:Consumer 消费 offset=6 的消息(HW=5 时)
如果允许消费:
Consumer 消费 offset=6 → Leader 宕机
→ Follower 成为新 Leader,但 Follower 没有 offset=6 的数据
→ Consumer 发现消息"消失",数据不一致
禁止消费:
Consumer 只能消费 offset < HW 的消息
→ 这些消息已被 ISR 全部副本写入
→ 即使 Leader 宕机,ISR 中任何一个 Follower 成为新 Leader 后,这些消息都不会丢失

本质一句话:HW 是数据安全的边界,HW 以下的消息已被 ISR 全部确认,即使 Leader 宕机也不会丢失。


Q5:HW 的推进为什么有延迟?这会导致什么问题?高频

Section titled “Q5:HW 的推进为什么有延迟?这会导致什么问题?”

回答要点

HW 推进的滞后性

HW 的推进不是实时的,需要两轮 Fetch 请求:

Round 1:Follower fetch
Follower → Leader: 请求 offset=8 开始的数据
Leader → Follower: 返回消息 + 当前 HW=5
Follower 写入本地,LEO=8
Round 2:Follower 再次 fetch
Follower → Leader: 请求 offset=8 开始的数据(汇报 LEO=8)
Leader 收到所有 ISR 的 LEO 汇报
Leader 推进 HW = min(8, 8, 8) = 8
Leader → Follower: 返回新 HW=8
Follower 更新 HW=8

时序图

时间轴:T1 → T2 → T3 → T4 → T5
T1: Leader LEO=8, HW=5
T2: Follower1 fetch,写入 LEO=8
T3: Follower2 fetch,写入 LEO=8
T4: Leader 收到所有 ISR 汇报,推进 HW=8
T5: 下次 fetch,Follower 更新 HW=8
延迟:T1 到 T5,可能持续数十毫秒到数百毫秒

导致的问题(HW 截断数据丢失)

场景(2副本,acks=1):
T1: Producer 写入 offset=0 到 Leader(A),A 的 LEO=1,HW=0
T2: Follower(B) fetch,复制 offset=0,B 的 LEO=1
T3: A 的 HW 更新为 1,但还没通知 B(延迟)
T4: B 宕机重启,B 的 HW 仍为 0
→ B 截断自己的 log 到 HW=0,删除了 offset=0 的消息!
T5: A 宕机,B 成为新 Leader
→ offset=0 的消息永久丢失

解决方案:Leader Epoch(详见下一个问题)


Q6:Leader Epoch 解决了什么问题?如何避免 HW 截断导致的数据丢失?高频

Section titled “Q6:Leader Epoch 解决了什么问题?如何避免 HW 截断导致的数据丢失?”

回答要点

HW 截断问题回顾

Follower 重启后,会将自己的日志截断到本地记录的 HW 位置。但 HW 推进有延迟,Follower 可能已经写入某些消息,但 HW 还没更新,重启后截断会误删这些消息。

Leader Epoch 的解决方案

每次 Leader 变更,Epoch(纪元)+1,每个 Epoch 记录该任期 Leader 写入的起始 offset:

Epoch 记录(存储在本地文件 leader-epoch-checkpoint)

Epoch=0, StartOffset=0 ← 第一任 Leader 从 offset=0 开始写
Epoch=1, StartOffset=5 ← 第二任 Leader 从 offset=5 开始写
Epoch=2, StartOffset=8 ← 第三任 Leader 从 offset=8 开始写

Follower 重启后的恢复逻辑(有了 Epoch)

旧逻辑(HW 截断):
Follower 重启 → 截断到本地 HW → 删除 HW 之后的消息
新逻辑(Epoch 截断):
1. Follower 重启 → 向 Leader 发送 OffsetForLeaderEpoch 请求
2. Leader 返回该 Epoch 的最后 offset
3. Follower 只截断到 Leader 确认的安全位置
4. 避免误删已经提交但 HW 还没推进的消息

代码示例(伪代码):

// Follower 重启后的恢复逻辑
void recover() {
int currentEpoch = readCurrentEpoch(); // 从 leader-epoch-checkpoint 读取
int leaderEpoch = fetchLeaderEpoch(); // 向 Leader 请求当前 Epoch
if (currentEpoch < leaderEpoch) {
// Epoch 不一致,需要截断
long endOffset = fetchEndOffset(currentEpoch);
truncateLog(endOffset); // 只截断到安全位置
} else {
// Epoch 一致,正常恢复
recoverFromHW();
}
}

对比表格

对比维度HW 截断Leader Epoch
截断依据本地 HWLeader 确认的 offset
数据安全性可能误删不会误删
一致性弱一致性最终一致性
引入版本0.8.x0.11+

本质一句话:Leader Epoch 用 Epoch + StartOffset 替代 HW 作为截断依据,避免误删已提交消息,彻底解决数据一致性问题。


链式追问三:acks 配置与数据可靠性

Section titled “链式追问三:acks 配置与数据可靠性”

Q7:acks 参数有哪些取值?acks=all 一定不会丢消息吗?必考

Section titled “Q7:acks 参数有哪些取值?acks=all 一定不会丢消息吗?”

回答要点

acks 取值对比

acks 值含义数据安全性能适用场景
0不等待任何确认,发完即走最低(可能丢消息)最高日志、监控
1等 Leader 写入确认中(Leader 宕机可能丢失)一般业务
all(或 -1等 ISR 内所有副本写入确认最高最低金融、支付

acks=all 的完整保障链

acks=all 配合 min.insync.replicas=2:
Producer 发消息
Leader 写入本地 log
ISR 中所有 Follower 写入(至少2个副本确认)
Leader 返回 ACK 给 Producer
即使此时 Leader 宕机,至少有一个 Follower 有完整数据
→ 新 Leader 选举后数据不丢失

acks=all 仍可能丢消息的场景

场景1:ISR 只有 Leader

ISR = {Leader}(其他副本都被踢出)
acks=all → 等价于 acks=1
Leader 宕机 → 数据丢失

场景2:磁盘损坏

ISR 中所有副本的磁盘同时损坏
→ 数据物理丢失

场景3:unclean 选举

ISR 全部宕机,out-of-sync 副本成为 Leader
→ 落后的消息丢失

配置组合推荐

# 最高可靠性(推荐金融/支付场景)
acks=all
min.insync.replicas=2 # ISR 至少2个副本才允许写入
replication.factor=3 # 总副本数3个
unclean.leader.election.enable=false
# 监控
UnderReplicatedPartitions > 0 → 告警

本质一句话:acks=all 需要配合 min.insync.replicas、replication.factor、unclean.leader.election.enable 三个参数,才能真正保证数据不丢失。


Q8:acks=all 和 min.insync.replicas=2 时,写入延迟是多少?如何优化?中频

Section titled “Q8:acks=all 和 min.insync.replicas=2 时,写入延迟是多少?如何优化?”

回答要点

写入延迟的组成

总延迟 = Leader 写入延迟 + Follower 同步延迟 + 网络往返延迟

性能数据(生产环境实测):

配置平均延迟99 分位延迟
acks=1~5ms~20ms
acks=all, min.insync.replicas=2~10ms~50ms
acks=all, min.insync.replicas=3~15ms~80ms

优化措施

1. 减少网络延迟

# 同机房部署
Broker1, Broker2, Broker3 部署在同一机房
→ 网络延迟 < 1ms
# 跨机房部署(延迟高)
Broker1 在机房A,Broker2 在机房B
→ 网络延迟 2-5ms

2. 优化磁盘 IO

Terminal window
# 使用 SSD
SSD 顺序写:~500MB/s
HDD 顺序写:~100MB/s
# 调整页缓存
# 为页缓存留出足够内存(机器内存的 60%)
# JVM 堆内存不超过 6GB

3. 批量发送

# Producer 配置
batch.size=16384 # 积累到 16KB 再发送
linger.ms=5 # 等待 5ms 后发送
compression.type=lz4 # 压缩减少网络传输
# 性能提升
单条发送:延迟 ~10ms,吞吐 10万条/s
批量发送:延迟 ~15ms,吞吐 100万条/s

4. 减少 ISR 副本数

min.insync.replicas=2 → 只需 2 个副本确认,不是所有副本
→ 减少等待最慢副本的时间

对比表格

优化措施延迟降低吞吐提升副作用
同机房部署显著(-5ms)
使用 SSD中(-2ms)显著成本高
批量发送略增(+5ms)极显著延迟增加
减少 ISR 副本数显著(-3ms)可靠性降低

链式追问四:Leader 选举与高可用

Section titled “链式追问四:Leader 选举与高可用”

Q9:Leader 宕机后,如何选举新 Leader?ISR 为空时怎么办?必考

Section titled “Q9:Leader 宕机后,如何选举新 Leader?ISR 为空时怎么办?”

回答要点

正常选举流程

Leader 宕机 → Controller 监听到 ZooKeeper/KRaft 的 Broker 下线事件
Controller 从 ISR 中选择一个 Follower 作为新 Leader
Controller 更新元数据,通知所有 Broker
新 Leader 开始处理读写,旧 Leader 恢复后成为 Follower

ISR 为空时的处理

场景:整个 ISR 都不可用(所有同步副本都宕机)

对比表格

配置行为数据安全可用性
unclean.leader.election.enable=false(默认)不从 out-of-sync 副本选主,等待 ISR 恢复零丢失分区不可用
unclean.leader.election.enable=true允许 out-of-sync 副本成为 Leader数据丢失分区立即恢复

unclean 选举示例

ISR = {Broker1, Broker2},都在 ISR 中
Broker1(Leader): offset=0-100
Broker2(Follower): offset=0-100
Broker1, Broker2 宕机,只有 Broker3 存活
Broker3(out-of-sync): offset=0-80(落后20条)
unclean.leader.election.enable=false:
→ 等待 Broker1 或 Broker2 恢复
→ 分区不可用,但数据不丢失
unclean.leader.election.enable=true:
→ Broker3 成为新 Leader
→ offset=81-100 的消息永久丢失
→ Consumer 发现消息"消失"(offset 81-100 不存在)
→ 分区立即恢复可用

工程选择

# 业务消息(订单/支付)
unclean.leader.election.enable=false
# 数据不能丢,宁可分区不可用
# 日志/监控
unclean.leader.election.enable=true
# 可容忍少量丢失,优先保障可用性

本质一句话:unclean 选举是可用性和一致性的权衡,业务消息选择一致性,日志监控选择可用性。


Q10:如何监控 Kafka 的副本同步健康状况?实战

Section titled “Q10:如何监控 Kafka 的副本同步健康状况?”

回答要点

关键监控指标

1. Under-Replicated Partitions(URP)

Terminal window
# JMX 指标
kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions
# 含义:ISR 缩水的分区数
# 告警阈值:> 0(任何分区 ISR 缩水都需要告警)

2. ISR Shrink/Expand 速率

Terminal window
# JMX 指标
kafka.server:type=ReplicaManager,name=IsrShrinksPerSec
kafka.server:type=ReplicaManager,name=IsrExpandsPerSec
# 含义:ISR 缩小/扩大的速率
# 告警阈值:频繁变更(> 1次/分钟)

3. Replica Lag

Terminal window
# JMX 指标
kafka.server:type=FetcherLagMetrics,name=ConsumerLag,partition=*
# 含义:Follower 相对 Leader 的消息数差距
# 告警阈值:> 10000(落后超过 1 万条)

4. Leader 选举频率

Terminal window
# JMX 指标
kafka.controller:type=ControllerStats,name=LeaderElectionRateAndTimeMs
# 含义:Leader 选举的频率和耗时
# 告警阈值:频繁选举(> 1次/分钟)

监控工具推荐

# Prometheus + Grafana
jmx_exporter:
- UnderReplicatedPartitions
- IsrShrinksPerSec
- IsrExpandsPerSec
- LeaderElectionRateAndTimeMs
# Grafana 仪表盘
可视化展示:
- URP 趋势图
- ISR 变更事件
- Follower Lag 排名

排查思路

现象可能原因排查方法
URP > 0Follower 不同步检查网络、磁盘、GC
ISR 频繁变更Follower 不稳定查看 GC 日志、网络延迟
Replica Lag 大Follower 处理慢检查磁盘 IO、网络带宽
Leader 频繁选举Broker 不稳定检查 Broker 日志、硬件故障

实战案例

问题:UnderReplicatedPartitions 从 0 增长到 50
排查:
1. 查看监控 → Broker2 的 Follower 全部离开 ISR
2. 检查 Broker2 日志 → GC 停顿长达 30 秒
3. 分析原因 → JVM 堆内存不足,Full GC 频繁
4. 优化措施 → 增大堆内存到 6GB,调整 GC 算法为 G1
结果:UnderReplicatedPartitions 恢复到 0

Q11:Follower 副本只同步数据,能处理消费请求吗?加分

Section titled “Q11:Follower 副本只同步数据,能处理消费请求吗?”

回答要点

默认情况

Kafka 的 Follower 只做数据同步,不处理任何客户端请求(生产或消费),所有读写都由 Leader 处理。

对比表格

对比维度Kafka FollowerMySQL 从库
读写请求只同步数据可处理读请求
数据一致性保证 HW 以下一致可能落后主库
设计目标高吞吐而非低延迟读读写分离,减轻主库压力

原因

  1. 数据可能落后:Follower 的数据可能落后 Leader(HW 之后的消息),让 Follower 处理消费可能导致消费者读到不一致的数据
  2. 简化设计:Kafka 追求高吞吐,而非低延迟读,Leader 处理所有请求简化了设计

Kafka 2.4+ 的 Follower Fetch(KIP-392)

引入了允许 Consumer 从最近的 Follower 读取数据的功能:

# Consumer 配置
client.rack=us-east-1a # 消费者所在机架
# Broker 配置
replica.selector.class=org.apache.kafka.common.replica.RackAwareReplicaSelector
# 效果
Consumer 优先从同机架的 Follower 读取数据
→ 降低跨机房流量
→ 降低网络延迟

适用场景

跨机房/跨可用区场景:
机房A: Leader(Broker1),Consumer1
机房B: Follower(Broker2),Consumer2
默认情况:
Consumer2 → 读取 Leader(跨机房)→ 延迟高、流量大
Follower Fetch:
Consumer2 → 读取 Follower(同机房)→ 延迟低、流量小

代价:Consumer 可能读到稍旧的数据(Follower 比 Leader 落后几条消息)


Q12:如果同一时刻所有 ISR 副本都宕机了,Kafka 怎么处理?如何避免?加分

Section titled “Q12:如果同一时刻所有 ISR 副本都宕机了,Kafka 怎么处理?如何避免?”

回答要点

场景分析

所有 ISR 副本同时宕机,这是 Kafka 高可用的边界场景,处理方式取决于配置:

处理方式对比

方式1:unclean.leader.election.enable=false(默认)

所有 ISR 副本宕机 → 该 Partition 完全不可用
Controller 持续等待 ISR 副本恢复
如果 ISR 副本数据损坏(磁盘故障)→ 分区永久不可用
需要运维介入,手动触发 unclean 选举或从备份恢复

方式2:unclean.leader.election.enable=true

从存活的 out-of-sync 副本中选一个成为 Leader
该副本落后的消息永久丢失
Consumer 会发现消息"消失"(offset 跳变)
分区立即恢复可用

工程预防

1. 多副本配置

replication.factor=3 # 3 副本,需 3 个副本同时故障才触发此场景
min.insync.replicas=2 # ISR 至少 2 个副本

2. 跨机房部署

同城双机房:
机房A: Broker1, Broker2
机房B: Broker3, Broker4
每个分区的副本分布在 2 个机房
→ 单机房故障不影响 ISR

3. 监控告警

Terminal window
# 监控 URP
UnderReplicatedPartitions > 0 告警
IsrShrinksPerSec > 0 告警
# 监控 Broker 存活
Broker 存活数 < 预期 告警

4. 定期备份

Terminal window
# 定期备份关键 Topic 的消息
kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list ... --topic orders --time -1
# 从备份恢复(极端情况)

本质一句话:多副本 + 跨机房部署 + 监控告警,避免所有 ISR 副本同时故障的极端场景。


总结:Kafka 高可用的面试答题思路

Section titled “总结:Kafka 高可用的面试答题思路”

ISR 机制

  • ISR 动态调整,replica.lag.time.max.ms 控制踢出条件
  • ISR 缩水时,min.insync.replicas 是数据安全的最后防线

HW/LEO 机制

  • HW 是数据安全的边界,Consumer 只能消费 HW 之前的消息
  • HW 推进有延迟,Leader Epoch 解决 HW 截断导致的数据丢失

acks 配置

  • acks=all 需配合 min.insync.replicas、replication.factor、unclean.leader.election.enable
  • 优化写入延迟:同机房部署、SSD、批量发送、减少 ISR 副本数

Leader 选举

  • 正常从 ISR 中选举,ISR 为空时根据 unclean.leader.election.enable 决定
  • unclean 选举是可用性和一致性的权衡

监控与容灾

  • 监控 UnderReplicatedPartitions、ISR 变更频率、Follower Lag
  • 多副本 + 跨机房部署避免极端故障场景

掌握这些链式追问的答案,你就能在 Kafka 高可用面试中拿高分!