3. Kafka高可用原理:ISR机制、Leader选举、ACKS配置对灾备的影响
聊灾备,绕不开Kafka的高可用机制。很多同学觉得Kafka高可用就是「多副本 + 自动切换」,真到生产环境一压测,问题全冒出来了。我见过不少团队,副本配了3个,acks设了all,结果机房断电后数据还是丢了——为什么?因为没搞懂ISR和Leader选举的底层逻辑。
这一节,咱们把ISR机制、Leader选举、ACKS配置这三个东西拆开揉碎。你理解透了,灾备方案才不会踩坑。
3.1 ISR机制:到底谁才算「同步副本」?
ISR的全称是In-Sync Replicas,翻译过来就是「在同步中的副本」。说白了,Kafka只认那些跟Leader保持同步的副本,不同步的副本会被踢出ISR集合。
这里有个关键点:不是所有副本都在ISR里。我遇到过有人把「副本数=3」理解成「3个副本随时都能切换」,这是错的。只有ISR里的副本才有资格接替Leader。
ISR的判定标准
Kafka怎么判断一个副本是否「同步」?看两个参数:
- replica.lag.time.max.ms:默认30秒。如果Follower超过30秒没向Leader发起同步请求,或者同步进度落后太多,就会被踢出ISR。
- min.insync.replicas:ISR的最小副本数。这个参数在灾备场景下特别重要,后面会细说。
举个例子,你配了副本数=3,min.insync.replicas=2。如果一台Broker挂了,ISR里只剩2个副本,生产还能正常写。但如果再挂一台,ISR只剩1个副本,这时候写入就会报错——嗯,这就是保护机制。
核心结论:ISR是动态变化的。副本数≠可用副本数,只有ISR里的副本才是「活着的、可用的」。
3.2 Leader选举:谁来决定新Leader?
Leader挂了怎么办?Kafka不会随便选一个Follower当Leader。选举逻辑其实很严谨,我当年第一次看源码时也觉得挺巧妙。
选举流程
- Controller(控制器Broker)检测到Leader心跳超时。
- Controller从ISR集合中选出一个副本作为新Leader。
- 如果ISR集合为空(所有副本都挂了),Kafka会启用 unclean.leader.election.enable 配置。
这里有个坑:unclean选举。如果这个参数设为true,Kafka会从非ISR的副本中选Leader。这意味着什么?数据可能不一致——那个副本可能落后Leader好几万条消息。我曾在一次机房故障中亲眼看到,unclean选举后,消费端读到的是旧数据,业务对账全乱了。
避坑指南:我曾经在生产环境吃过这个亏。当时为了「高可用优先」,把unclean.leader.election.enable设成了true。结果一次网络抖动触发了unclean选举,数据回退了将近10分钟。后来我改成false,宁可短暂不可用,也不能丢数据。灾备场景下,我建议你根据业务容忍度来权衡。
Controller的角色
Kafka集群里有一个Controller,负责管理分区和副本的状态。Controller本身也是通过ZooKeeper选举出来的。如果Controller挂了,其他Broker会重新竞选。
你想想看,整个集群的元数据管理都依赖Controller。如果Controller压力过大,会影响Leader选举的速度。我见过一个集群,分区数上万,Controller频繁GC,导致Leader切换耗时超过30秒——这在灾备场景下是不可接受的。
3.3 ACKS配置:数据可靠性的最后一道防线
ACKS参数决定了Producer等待多少个副本确认消息写入成功。这个参数直接影响灾备能力。
| ACKS值 | 行为 | 可靠性 | 性能 |
|---|---|---|---|
| acks=0 | Producer发完就认为成功,不等待任何确认 | 最低,可能丢数据 | 最高 |
| acks=1 | 等待Leader确认写入成功 | 中等,Leader挂了可能丢数据 | 较高 |
| acks=all | 等待ISR中所有副本确认写入成功 | 最高,几乎不丢数据 | 较低 |
灾备场景下,我建议你使用acks=all。但要注意,acks=all不是万能的。它依赖ISR集合的大小。如果min.insync.replicas=1,即使acks=all,只要Leader确认了就算成功——因为ISR里只有Leader自己。这跟acks=1没区别。
我的经验:生产环境我一般这样配:副本数=3,min.insync.replicas=2,acks=all。这样即使一台Broker挂了,还能保证至少2个副本同步写入。性能损失大概在20%-30%,但换来的是数据不丢的底气。
3.4 三者如何影响灾备?
ISR、Leader选举、ACKS不是孤立的。它们共同决定了Kafka在故障时的行为。我画了一张图,帮你理清它们的关系:
灾备场景推演
咱们推演一个典型场景:机房A的Leader挂了,机房B的Follower在ISR里。
- 如果acks=all,min.insync.replicas=2:机房B的Follower已经同步了数据,选举后数据完整,消费端无感知。
- 如果acks=1:机房A的Leader确认了写入,但机房B的Follower可能还没同步。Leader挂了,数据丢了。
- 如果unclean选举开启:即使ISR为空,也能从落后副本中选Leader,但数据可能回退。
说白了,灾备不是靠某一个参数搞定的。ISR保证副本同步的「质量」,ACKS控制写入的「确认范围」,Leader选举决定故障后的「恢复路径」。三者配合,才能做到真正的数据不丢、服务不停。
我的建议:灾备场景下,请记住这个黄金组合——副本数≥3,min.insync.replicas=2,acks=all,unclean.leader.election.enable=false。这是我在多个生产集群验证过的配置,能扛住单机房故障。
3.5 常见误区与避坑
最后列几个我经常看到的误区,你对照一下自己的配置:
- 误区一:副本数=3就以为数据一定有三份。实际上,如果ISR里只有2个副本,第3个副本可能已经落后很多了。
- 误区二:acks=all就以为数据一定不丢。如果min.insync.replicas=1,acks=all跟acks=1没区别。
- 误区三:Leader选举很快,几秒就能恢复。如果分区数多、Controller压力大,切换时间可能超过30秒。
嗯,这些坑我都踩过。你提前知道,就能少走弯路。