上一篇【第60篇】Kafka KRaft模式解析——告别ZooKeeper的新时代来了下一篇【第62篇】Kafka数据不丢失实战指南——生产者、Broker、消费者三端防护摘要在分布式系统中谈可靠性就像在暴风雨中谈房子不漏水——难但不是做不到。Kafka的可靠性体系由三个核心组件构成acks确认机制、ISR同步副本集、min.insync.replicas最小同步副本数。这三个东西单看都不难难的是把它们组合成一套既可靠又不牺牲吞吐的铁三角。本文用图解对比的方式深入解析这三个组件的原理和协同机制。读完你就明白为什么有人acks0消息满天飞但丢了也不心疼为什么有人acksall还丢了数据以及怎么配出一个钱和命都要的生产级配置。一、可靠性保证的三大基石先上一张全景图搞清楚三个组件分别负责什么【Kafka可靠性铁三角】 生产者 Producer │ │ 发送消息 │ ① acks? ▼ ┌──────────────────────┐ │ Kafka Broker │ │ │ │ ┌───┐ ┌───┐ ┌───┐│ │ │ L │ │ F │ │ F ││ │ │ │ │ │ │ ││ │ └───┘ └───┘ └───┘│ │ ▲ │ │ │ │ │ ② ISR [L, F1, F2]│ │ │ │ ③ min.insync │ │ .replicas 2 │ └──────────────────────┘ ① acks 生产者要求多少个副本确认才算成功 ② ISR 哪些副本是跟得上节奏的同步副本 ③ min.isr 最少需要多少个同步副本才允许写入这三个参数的关系可以用一句话概括acks定义了什么是成功ISR定义了谁有资格参与成功min.isr定义了最少多少人参与才能开始干活。二、acks——生产者的安心程度acks是生产者的核心配置决定了一条消息发出去什么情况下算成功什么情况下算失败。它有三个取值【acks 三种模式对比图】 Leader节点 Follower节点 acks0 ──────────► □ 直接返回成功不管写没写 (发完就跑) Producer acks1 ──────────► ■ Leader写入 → 返回成功 (主子说了算) Producer │ │ 异步复制 ▼ □ Follower不一定同步 acksall/-1 ──────────► ■ Leader写入 (全员通过) Producer │ ┌──▼─────────────┐ │ 等待所有ISR确认 │ │ ■ Follower1同步 │ │ ■ Follower2同步 │ └────────────────┘ │ ▼ 所有ISR确认后 返回成功 ✓2.1 acks0 —— “消息我发出去了收没收到别问我”行为Producer发送消息后不等待任何确认直接返回成功可靠性最低。Broker还没收到消息Producer就已经说成了适用场景日志采集、监控指标等丢几条无所谓的场景吞吐量最高但这是用可靠性换来的// acks0 配置PropertiespropsnewProperties();props.put(acks,0);// 这种配置下Producer发完消息就不管了// Broker挂掉 → 消息丢失 → Producer完全不知道2.2 acks1 —— “Leader说收到就算Follower随意”行为Leader Partition写入消息后立即返回确认不等Follower可靠性中等。如果Leader在Follower同步前宕机消息丢失适用场景对实时性要求高允许少量丢失的业务典型故障Leader刚写完就挂新Leader原Follower没这条消息【acks1 的丢失场景】 时间轴 1. Producer → Leader写入 msg100 2. Leader写入成功立即返回 ACK 给 Producer 3. Producer收到 ACK认为 msg100 已安全 4. Leader还没来得及同步给 Follower宕机 5. Follower没有 msg100被选为新 Leader 6. 结果msg100 永久丢失 ← Producer还以为成功了2.3 acksall或 -1—— “所有人都点头了才算成”行为Leader写入后等待所有ISR副本都同步完成才返回确认可靠性最高。只要至少一个ISR副本活着数据就不丢适用场景订单、支付、交易等不容有失的核心业务注意这里的all不是所有副本而是所有ISR副本// acksall 配置PropertiespropsnewProperties();props.put(acks,all);// 等同 props.put(acks, -1);// 这条配置的意思是// Leader写入后等所有ISR副本都确认收到再告诉我成功// 任何一个ISR副本没确认就重试三、ISR——“谁有资格投票”3.1 ISR 是什么ISRIn-Sync Replicas是一组与Leader保持同步的Follower副本集合。只有ISR中的副本才有可能在Leader宕机后成为新Leader。【ISR 示意图】 Topic: orders, Partition: 0, Replication Factor: 3 ┌─────────────────────────────────────┐ │ ISR [B1, B2] │ │ │ │ Broker1 (Leader) Broker2 │ Broker3 │ ┌──────────────┐ ┌──────────┐ │ ┌──────────────┐ │ │ [msg1] │ │ [msg1] │ │ │ [msg1] │ │ │ [msg2] │ │ [msg2] │ │ │ │ │ │ [msg3] │ │ [msg3] │ │ │ [msg3] │ │ │ [msg4] │ │ [msg4] │ │ │ │ ◄── 丢了 msg2、msg4 │ │ [msg5] ──────▶── [msg5] │ │ │ [msg5] ⏰ 追不上 │ │ LEO5 │ │ LEO5 │ │ │ LEO5 (慢!) │ └──────────────┘ └──────────┘ │ └──────────────┘ │ │ └─────────────────────────────────────┘ Broker3 被踢出ISR ❌3.2 ISR 的动态维护ISR不是一成不变的它随着Follower的同步状态动态变化Follower追上Leader→ 加入ISRFollower落后超过replica.lag.time.max.ms默认30秒→ 被踢出ISR【ISR 动态变化过程】 时间 T0 T1 T2 T3 T4 ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ Broker1│ L │ │ L │ │ L │ │ L⬇ │ │ │ Broker2│ F✓ │ │ F✓ │ │ F⏰ │ │ L⬆ │ │ L │ Broker3│ F✓ │ │ F✓ │ │ F✓ │ │ F✓ │ │ F✓ │ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ ISR: [B1,B2, [B1,B2, [B1,B3] [B2,B3] [B2,B3] B3] B3] T0: 正常状态3个ISR T1: Broker2 网络抖动开始落后 T2: 30秒后Broker2 被移出ISR → ISR [B1, B3] T3: Broker1 宕机Broker2 追上来了当选新Leader T4: 系统恢复ISR [B2, B3] 结果如果 acksall min.isr2T2之后写入任然安全因为B1和B3还在3.3 ISR vs AR vs OSR概念含义说明AR(Assigned Replicas)分配的所有副本创建Topic时指定的 replication-factorISR(In-Sync Replicas)同步副本集AR的子集与Leader保持同步OSR(Out-of-Sync Replicas)滞后副本集AR - ISR落后太多的副本三个集合的关系OSR ISR AR四、min.insync.replicas —— “最少几个副本人齐了才能开工”4.1 为什么需要 min.isracksall 虽然等所有ISR确认但有一个致命问题如果ISR只剩一个副本Leader自己acksall 等同于 acks1消息丢失风险陡然升高。【没有 min.isr 的风险场景】 Topic: orders, RF3, acksall ┌──────────────────────────────────┐ │ Broker1 (Leader) ✅ 在线 │ │ Broker2 (Follower) ❌ 宕机 │ │ Broker3 (Follower) ❌ 宕机 │ │ │ │ ISR [Broker1] ← 只剩Leader │ │ │ │ Producer 发送 msg100 │ │ → Leader写入成功 │ │ → Leader返回ACK (因为ISR全确认了)│ │ → 下一秒 Broker1 宕机 │ │ → msg100 永久丢失 │ └──────────────────────────────────┘ 这就是 acksall 也丢消息的经典场景加上 min.insync.replicas【有 min.isr 保护】 min.insync.replicas 2 ┌──────────────────────────────────┐ │ Broker1 (Leader) ✅ 在线 │ │ Broker2 (Follower) ❌ 宕机 │ │ Broker3 (Follower) ❌ 宕机 │ │ │ │ ISR [Broker1] ← 不满足 min.isr2 │ │ │ Producer 发送 msg100 │ │ → Broker 返回错误: │ │ NOT_ENOUGH_REPLICAS │ │ → Producer 拒绝写入 │ │ → msg100 虽然没发成功但没丢 │ └──────────────────────────────────┘4.2 配置范围# Broker 端配置全局默认 min.insync.replicas2 # Topic 级别覆盖 kafka-configs.sh --alter --topic orders \ --add-config min.insync.replicas2 \ --bootstrap-server localhost:9092规则min.insync.replicas ≤ replication-factorRF推荐 min.isr可靠性等级说明11低只能容忍0个故障22中1个ISR故障就不可写32高容忍1个ISR故障仍可写入33最高任何1个ISR故障就不可写CA要求AP53很强跨机房部署容忍2个机房故障推荐配置RF3min.isr2—— 均衡可靠性和可用性。五、三剑客的协同——场景推演5.1 配置组合表acksmin.isrRF数据丢失容忍度写入可用性适用场景0-1丢得很坦然最高日志指标丢了不心疼113可能丢1条高一般业务all13ISR1时可能丢高你不推荐all23基本不丢中1个ISR故障仍可写核心业务推荐all33不丢但不可写低1个ISR故障就拒绝写入金融级要CA5.2 最佳实践场景推演【推荐配置RF3, acksall, min.isr2】 正常状态 ┌─────────────────────────────────────┐ │ Broker1 (Leader) Broker2 Broker3 │ ■ msg1~100 ■ msg1~100 ■ msg1~100 │ │ │ ISR [B1, B2, B3], min.isr2 ✓ └─────────────────────────────────────┘ Producer 正常发送等待所有ISR确认后返回成功 Broker2 宕机 ┌─────────────────────────────────────┐ │ Broker1 (Leader) Broker2 Broker3 │ ■ msg1~100 ■ msg1~100 │ │ │ ISR [B1, B3], min.isr2 ✓ 仍可写入 └─────────────────────────────────────┘ ISR满足min.isr写入继续 Broker2 和 Broker3 同时宕机 ┌─────────────────────────────────────┐ │ Broker1 (Leader) Broker2 Broker3 │ ■ msg1~100 │ │ │ ISR [B1], min.isr2 ✗ 拒绝写入 └─────────────────────────────────────┘ Producer收到 NOT_ENOUGH_REPLICAS 错误 数据宁可发不出去也不冒险丢 问题所有人都不可写对这是有意的设计 宁可拒绝写入也不接受假成功六、实战配置推荐与代码示例6.1 高可靠配置推荐核心业务使用// 生产者端配置PropertiespropsnewProperties();props.put(bootstrap.servers,localhost:9092);props.put(key.serializer,org.apache.kafka.common.serialization.StringSerializer);props.put(value.serializer,org.apache.kafka.common.serialization.StringSerializer);// 可靠性核心配置 props.put(acks,all);// 必须等所有ISR确认props.put(retries,Integer.MAX_VALUE);// 无限重试新版默认props.put(max.in.flight.requests.per.connection,5);// 配合幂等性保证顺序props.put(enable.idempotence,true);// 开启幂等性防止重复// 可靠性辅助配置 props.put(delivery.timeout.ms,120000);// 发送总超时含重试props.put(request.timeout.ms,30000);// 单次请求超时KafkaProducerString,StringproducernewKafkaProducer(props);// 发送时使用带回调的方式感知发送结果producer.send(newProducerRecord(orders,key,value),(metadata,exception)-{if(exception!null){// 发送失败需要记录日志、落盘兜底、或者告警log.error(消息发送失败! topic{}, key{},metadata.topic(),exception);// 降级方案写入本地文件后续重试 —— 这是防止消息丢失的最后防线}});# Broker 端 topic 配置 # 创建 topic 时指定 kafka-topics.sh --create \ --topic orders \ --partitions 6 \ --replication-factor 3 \ --config min.insync.replicas2 \ --config unclean.leader.election.enablefalse \ --bootstrap-server localhost:90926.2 高吞吐配置日志、监控等场景PropertiespropsnewProperties();props.put(acks,0);// 不等确认极速props.put(linger.ms,100);// 攒一批再发减少网络开销props.put(batch.size,65536);// 批量 64KBprops.put(compression.type,lz4);// 压缩省带宽// 注意这种配置下消息可能丢失只适用于日志等非核心场景6.3 平衡配置大多数业务场景PropertiespropsnewProperties();props.put(acks,all);// 数据可靠props.put(retries,5);// 重试5次props.put(max.in.flight.requests.per.connection,1);// 严格保序props.put(compression.type,snappy);// 压缩// Broker 端:// min.insync.replicas 2// replication.factor 3// unclean.leader.election.enable false七、常见误区与排查误区一acksall 就100%不丢消息❌ 错误认知acksall 消息绝对不会丢 ✅ 真相ISR 只剩 Leader 时acksall 等同于 acks1 → 需要配合 min.insync.replicas ≥ 2 才能真正保证误区二min.isr 设大了集群就没用了❌ 错误认知min.isr3 时任意节点挂了集群就不可写 ✅ 真相只要 ISR 数量 ≥ 3不是 3 个副本全在 ISR 里 → Follower 挂掉会被踢出 ISRLeader 和另一个 Follower 够 2 就行 → 但是如果 min.isr3 且 replica.lag.time.max.ms 设置太长 来不及踢出落后 Follower可能导致系统不可写误区三unclean.leader.election.enable 无所谓unclean.leader.election.enable false默认推荐 → 只有 ISR 副本可以当选 Leader → 保证了当选的 Leader 有全量数据 unclean.leader.election.enable true危险 → OSR 副本也可以当选 Leader不干净的选举 → 可能导致数据丢失 场景 Leader(B1) Follower1(B2) 都挂了只剩 B3OSR里落后10分钟 如果 uncleantrue → B3 当选 Leader → 丢失10分钟的数据 如果 uncleanfalse → 分区不可用等待至少一个ISR副本恢复 推荐除非你能接受丢数据否则永远设 false本篇小结今天我们拆解了Kafka可靠性保证的三大组件acks定义了成功的含义——0是我说发了就算1是Leader说写了就算all是所有ISR都写了才算ISR动态维护的靠谱副本俱乐部——跟得上的进来掉队的出去min.insync.replicas写入的入场人数下限——人数不够就拒绝入场宁可不开工也不冒险三者的黄金组合acksall min.isr2 RF3 uncleanfalse这是生产环境的最佳实践。下一篇我们将深入实战层面从生产者、Broker、消费者三个维度手把手教你怎么真正实现消息一条都不丢。上一篇【第60篇】Kafka KRaft模式解析——告别ZooKeeper的新时代来了下一篇【第62篇】Kafka数据不丢失实战指南——生产者、Broker、消费者三端防护