4、副本机制详解:Leader与Follower、ISR机制、acks参数对可靠性的影响

副本机制是Kafka高可用的基石。说白了,没有副本,Kafka就是个高性能消息队列,但一挂就全完。我刚开始用Kafka时,觉得副本就是个备份,后来线上出过一次事故才明白——副本的细节远比想象中复杂。

这一节,咱们把副本机制拆开揉碎了讲。从Leader和Follower怎么配合,到ISR这个核心概念,再到acks参数怎么调。每个点我都会结合实战经验来说。

4.1 Leader与Follower:谁说了算?

每个分区可以有多个副本,但只有一个Leader。所有读写请求都走Leader,Follower只做一件事:从Leader拉数据,保持同步。

为什么这么设计?你想想看,如果每个副本都能写,那数据一致性就乱套了。Leader负责写,Follower负责同步,这个模型简单又可靠。

核心原则:生产者只写Leader,消费者只读Leader。Follower不对外提供服务,只做数据备份。

我在项目中遇到过一个问题:某个分区的Leader挂了,集群自动选了一个新的Leader。但新Leader的数据比旧Leader少了几个消息。为什么?因为Follower的同步有延迟。这就引出了ISR机制。

4.2 ISR机制:谁是"同步"的?

ISR全称是In-Sync Replicas,翻译过来就是"在同步中的副本"。不是所有Follower都能进ISR,只有那些跟Leader差距不大的副本才行。

判断标准有两个参数:

  • replica.lag.time.max.ms:默认30秒。Follower超过这个时间没向Leader发同步请求,就被踢出ISR。
  • replica.lag.max.messages:这个参数在0.9版本后已经废弃了。为什么?因为消息大小不一样,用数量判断不准确。

ISR的作用是什么?Leader选举时,只有ISR里的副本才有资格成为新Leader。为什么?因为ISR里的副本数据跟Leader基本一致,选它们不会丢数据。

我的建议:replica.lag.time.max.ms别设太大。我见过有人设成5分钟,结果Leader挂了,新Leader的数据落后了一大截。一般设10-30秒比较合理。

咱们用一张图来理解ISR的运作流程:

ISR机制运作流程 生产者 写入消息 Leader 消费者 读取消息 ISR 集合(In-Sync Replicas) Follower 1 Follower 2 Follower 3 (落后,不在ISR) 同步 同步 同步超时 说明:Follower 1 和 Follower 2 在 replica.lag.time.max.ms 内完成了同步, 属于 ISR。Follower 3 同步超时,被踢出 ISR,没有选举资格。

4.3 acks参数:你要多可靠?

acks参数控制生产者写消息时,要等多少个副本确认才算成功。这个参数直接影响可靠性和性能的平衡。

有三种取值:

acks值 含义 可靠性 性能 适用场景
acks=0 生产者发完就认为成功,不等待任何确认 最低 最高 日志、监控等可丢数据
acks=1 等待Leader确认,不等待Follower 中等 较高 大部分业务场景
acks=all 等待所有ISR副本确认 最高 最低 金融、订单等不能丢数据

我一个个来说。

acks=0:发完就跑

生产者把消息扔到网络里,不管Kafka收没收到,直接返回成功。如果网络抖了一下,消息丢了,生产者完全不知道。

什么时候用?我做过一个日志采集系统,每天几十亿条日志,丢个万分之一完全能接受。这时候用acks=0,吞吐量能翻好几倍。

注意:千万别在核心业务上用acks=0。我曾经见过一个支付系统用了acks=0,结果网络抖动丢了订单消息,对账对了一周才补回来。

acks=1:Leader确认就行

生产者等Leader把消息写入本地日志后,就返回成功。Follower同没同步,不管。

这是默认值,也是大多数场景的选择。性能不错,可靠性也还行。但有个坑:Leader刚确认完就挂了,Follower还没同步,这条消息就丢了。

acks=all:最安全,也最慢

生产者要等所有ISR副本都写入成功,才返回成功。只要ISR里至少有一个副本活着,数据就不会丢。

配合min.insync.replicas参数一起用。这个参数指定ISR里最少要有几个副本,才能写数据。比如设成2,如果ISR里只剩1个副本,生产者会报错。

我的配置建议:

  • 副本数设3,min.insync.replicas设2,acks设all
  • 这样能容忍1个副本挂掉,还能正常写
  • 如果副本数设2,min.insync.replicas设2,那只要挂1个副本,整个分区就不能写了

4.4 实战:acks参数怎么选?

我整理了一个决策流程,你可以照着来:

  1. 数据能丢吗?能丢 → acks=0,追求极致性能
  2. 不能丢,但偶尔丢一点也行?→ acks=1,默认配置
  3. 一条都不能丢?→ acks=all,配合min.insync.replicas

举个例子。我做过一个订单系统,要求数据零丢失。配置是这样的:

# 生产者配置
acks=all
retries=3
max.in.flight.requests.per.connection=1
enable.idempotence=true

# Broker配置
default.replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false

注意最后那个参数:unclean.leader.election.enable。如果设为true,允许ISR之外的副本成为Leader。这会导致数据丢失,但能保证可用性。我一般设成false,宁可不可用,也不能丢数据。

4.5 避坑指南

这些年踩过的坑,分享几个给你:

  • 坑一:副本数设了3,但min.insync.replicas设了3。结果一台Broker挂了,所有分区都不能写。后来我改成2,留点余量。
  • 坑二:acks=all但retries设了0。网络抖动导致写入失败,消息直接丢了。retries至少要设3次。
  • 坑三:max.in.flight.requests.per.connection设太大,跟acks=all一起用会导致消息乱序。如果要求顺序,设成1。

副本机制就这些核心点。Leader和Follower的分工、ISR的筛选逻辑、acks的三种模式,搞懂了这些,Kafka的可靠性你就掌握了八成。剩下的两成,咱们后面讲数据一致性和故障恢复时再补上。