5月28日 08:26

Kafka 的副本机制是如何工作的?

副本机制的核心作用

Kafka 的副本机制解决的是分布式环境下的两个根本问题:数据可靠性和服务可用性。每个 Partition 可以配置多个副本(Replica),分布在不同 Broker 上。当某个 Broker 宕机,其他副本可以继续提供服务,保证消息不丢失、服务不中断。

副本因子(replication.factor)决定了每个 Partition 有多少个副本。生产环境通常设置为 3,意味着每个 Partition 有 3 份相同数据,允许最多 2 个节点故障而不丢数据。

Leader 与 Follower 的分工

Kafka 的副本采用 Leader-Follower 模型:

  • Leader 副本:每个 Partition 只有 1 个 Leader,负责处理该 Partition 的所有读写请求。Producer 写消息、Consumer 读消息都直接与 Leader 交互。
  • Follower 副本:被动地从 Leader 拉取数据并写入本地日志,不对外提供读写服务。Follower 的唯一职责是保持与 Leader 的数据同步,以便在 Leader 故障时接管。

这种设计的优势在于读写都走 Leader,避免了多副本写入的一致性问题,同时也简化了 Consumer 的消费逻辑——不需要关心从哪个副本读取。

ISR 机制:同步副本的动态管理

ISR(In-Sync Replicas)是 Kafka 副本机制中最关键的概念之一。它不是静态的副本列表,而是一个由 Leader 动态维护的同步副本集合。

ISR 的判定标准

Follower 是否留在 ISR 中,取决于它是否在规定时间内与 Leader 保持同步。判定依据是时间而非消息条数——早期 Kafka 用 replica.lag.max.messages 判定(已在 0.9.0 版本移除),现在只用 replica.lag.time.max.ms(默认 10 秒)。如果一个 Follower 超过这个时间没有发送拉取请求或虽然发送了但还没追上 Leader 的 LEO,就会被移出 ISR,进入 OSR(Out-of-Sync Replicas)。

AR、ISR、OSR 的关系

shell
AR(Assigned Replicas)= ISR + OSR

AR 是 Partition 分配的所有副本集合,ISR 是与 Leader 同步的副本,OSR 是落后于 Leader 的副本。理想状态下 ISR = AR,即所有副本都在同步。当 ISR 缩小时,说明集群出现了同步延迟。

ISR 与消息可靠性

min.insync.replicas 配合 Producer 的 acks=all 使用时,能提供强可靠性保证。当 ISR 中的副本数小于 min.insync.replicas 时,Broker 会拒绝写入,抛出 NotEnoughReplicasException。这是一种宁可不可用也不丢数据的策略。

典型配置:replication.factor=3 + min.insync.replicas=2 + acks=all,允许 1 个节点故障仍能正常写入。

HW 与 LEO:副本同步的位置标记

理解副本同步,必须搞清楚两个核心位移标记:

  • LEO(Log End Offset):每个副本(包括 Leader 和 Follower)各自维护的日志末端位移,表示下一条待写入消息的位置。每个副本的 LEO 可能不同。
  • HW(High Watermark):所有 ISR 副本中最小的 LEO。Consumer 只能消费到 HW 之前的消息,HW 之后的消息对 Consumer 不可见。

同步过程中 HW 和 LEO 的变化

  1. Producer 向 Leader 写入消息,Leader 的 LEO 递增
  2. Follower 从 Leader 拉取消息,Follower 的 LEO 递增
  3. Leader 收到所有 ISR 副本的 LEO 更新后,推进 HW(取所有 ISR 副本 LEO 的最小值)
  4. Follower 在下一次拉取时获取 Leader 的 HW,更新自己的 HW

这个过程确保了:HW 之前的消息已经被所有 ISR 副本确认,是安全可消费的

Leader 选举:故障恢复的核心流程

当 Leader 所在 Broker 宕机,Controller 会从 ISR 中选出一个新的 Leader。选举过程不是"投票",而是 Controller 直接指定。

选举策略

Kafka 的 Leader 选举策略根据触发场景不同分为四种:

策略触发场景选举逻辑
OfflinePartitionLeader Broker 宕机优先从 ISR 中选第一个存活的副本
ReassignPartition分区副本重分配从新 AR 中选第一个在线且在 ISR 中的副本
PreferredReplica自动均衡选 AR 中的第一个副本(如果在线且在 ISR 中)
ControlledShutdownBroker 优雅关闭选 ISR 中不在关闭 Broker 上的第一个副本

Unclean 选举:可用性与一致性的权衡

当 ISR 为空(所有副本都不同步)时,是否允许从 OSR 中选举 Leader?这由 unclean.leader.election.enable 控制:

  • 开启(默认 false):允许从 OSR 选 Leader,保证可用性,但可能丢数据——因为 OSR 副本的消息落后于原 Leader
  • 关闭:分区不可用直到原 Leader 恢复,保证数据一致性

金融场景通常关闭此选项,宁可短暂不可用也不冒数据丢失的风险。

Leader Epoch 解决的问题

早期 Kafka 依赖 HW 截断日志来保证副本一致性,但这会导致数据不一致问题。Kafka 0.11 引入了 Leader Epoch 机制:每个 Partition 维护一个单调递增的 Epoch 编号,新 Leader 产生时 Epoch 递增。Follower 用 Leader Epoch 而非 HW 来判断截断位置,避免了 HW 截断导致的数据丢失和分歧。

典型场景:两个 Follower 先后重启,旧 HW 截断可能导致先重启的 Follower 把已提交的消息截掉,而后重启的 Follower 又从前者拉取到不完整数据。Leader Epoch 通过记录每个 Epoch 对应的起始位移,让 Follower 精确知道从哪里截断。

副本同步的完整流程

  1. Producer 发送消息到 Leader:消息写入 Leader 的本地日志,Leader LEO 递增
  2. Follower 拉取消息:Follower 主动向 Leader 发送 FetchRequest,携带自己的 LEO
  3. Leader 返回消息:Leader 根据 Follower 的 LEO 返回对应数据,同时返回 Leader 当前的 HW
  4. Follower 写入并更新:Follower 将消息写入本地日志,更新 LEO,然后更新 HW(取 min(LEO, Leader HW))
  5. Leader 推进 HW:Leader 在收到 Follower 的下一次拉取请求时,根据所有 ISR 副本的 LEO 更新 HW

注意:Follower 是主动拉取而非 Leader 推送,这是 Kafka 副本同步与很多其他系统(如 MySQL 主从)的区别。拉取模式让 Follower 自己控制同步节奏,避免被 Leader 压垮。

副本分配与机架感知

创建 Topic 时,Kafka 自动分配副本到不同 Broker。分配算法考虑两个原则:

  • 同一 Partition 的副本分布在不同 Broker 上
  • 开启机架感知(broker.rack 配置)后,副本尽量分布在不同机架

机架感知的意义在于:如果整个机架故障(如电源故障),其他机架上的副本仍可用。不配置机架信息时,Kafka 只保证 Broker 级别分布,无法防御机架级故障。

properties
# Broker 机架配置 broker.rack=rack1 # 副本因子 default.replication.factor=3 # 最小同步副本数 min.insync.replicas=2

关键配置参数一览

参数默认值说明
replication.factor1副本数,生产环境建议 ≥ 3
min.insync.replicas1最小同步副本数,配合 acks=all 使用
acks1Producer 确认级别:0/1/all
replica.lag.time.max.ms10000Follower 落后超时时间
unclean.leader.election.enablefalse是否允许非 ISR 副本当选 Leader
auto.leader.rebalance.enabletrue是否自动均衡 Leader 到 Preferred 副本

监控核心指标

排查副本相关问题时,重点关注以下 JMX 指标:

  • UnderReplicatedPartitions:ISR 副本数 < AR 副本数的 Partition 数量,大于 0 说明有副本同步滞后
  • OfflinePartitionsCount:没有 Leader 的 Partition 数量,大于 0 说明有分区不可用
  • IsrShrinksPerSec / IsrExpandsPerSec:ISR 缩减和扩张速率,频繁变动说明集群不稳定
  • ActiveControllerCount:应该始终为 1,大于 1 说明有脑裂风险

生产环境实践建议

副本数不是越多越好。3 副本能满足大多数场景的可靠性要求,增加到 5 或 7 会显著降低写入吞吐(更多副本需要同步)并增加存储成本。只在极少数场景(如金融核心链路)才需要更高副本数。

务必开启 min.insync.replicas。只配 acks=all 不够——如果 ISR 缩减到只剩 Leader,acks=all 等于 acks=1,此时 Leader 宕机仍会丢数据。min.insync.replicas=2 确保至少 2 个副本确认才算写入成功。

关注 ISR 抖动。ISR 频繁缩扩通常不是正常波动,往往暗示网络延迟、磁盘 IO 瓶颈或 GC 问题。收到 ISR 缩减告警时,先排查 Follower 所在 Broker 的负载和延迟。

优雅下线优于故障下线。使用 kafka-reassign-partitions 先迁移 Leader 和副本,再下线 Broker,可以避免不必要的 Leader 选举和数据恢复开销。

标签:Kafka