4. 副本机制详解
副本机制是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的运作流程:
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参数怎么选?
我整理了一个决策流程,你可以照着来:
- 数据能丢吗?能丢 → acks=0,追求极致性能
- 不能丢,但偶尔丢一点也行?→ acks=1,默认配置
- 一条都不能丢?→ 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的可靠性你就掌握了八成。剩下的两成,咱们后面讲数据一致性和故障恢复时再补上。