Kafka 如何保证消息不丢失?

#中间件#面试八股

Kafka 如何保证消息不丢失?

结论:Kafka 的不丢要三段同时成立——生产端确认(Acknowledgement)到位(acks=all + 幂等 + 重试)、Broker 端副本够且不允许脏选举(副本 3、min.insync.replicas=2、unclean.leader.election=false)、消费端处理成功才提交位点。Kafka 的默认值在这三处都"偏性能",不显式改就会丢。

一、丢失点与默认值陷阱

Producer ──① 缓冲区满/超时被丢弃──> Leader ──② 只写页缓存,未同步到副本 ──> Follower
                                        └──③ ISR 缩到 1 时 acks=all 形同虚设
Consumer ──④ 先提交位点后处理 ──> 处理失败,位点已前移 ──> 丢
配置默认值风险建议
acksall(3.0 起)老版本默认 1,Leader 挂即丢显式写 all
enable.idempotencetrue(3.0 起)老版本关闭,重试产生重复/乱序显式开启
retries2147483647配合 delivery.timeout.ms 才安全保持默认,调大总超时
min.insync.replicas1ISR 只剩 Leader 时写入仍成功设为 2
unclean.leader.election.enabletrue落后副本被选为 Leader,静默丢数据设为 false
enable.auto.committrue提交时机不可控设为 false,手动提交
auto.offset.resetlatest位点丢失后从最新开始,中间消息被跳过关键业务设 earliest 并告警

二、三段的具体配置

# 生产端
acks=all
enable.idempotence=true
max.in.flight.requests.per.connection=5
delivery.timeout.ms=120000
buffer.memory=67108864
# Broker 端(server.properties)
default.replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false
log.flush.interval.messages=10000        # 默认靠副本冗余,不必每条 fsync
# 消费端
enable.auto.commit=false
auto.offset.reset=earliest
max.poll.records=200                     # 控制单批处理时长,避免超时被踢

三、消费端提交位点的正确姿势

while (running) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(200));
    for (ConsumerRecord<String, String> r : records) {
        biz.handle(r);                    // 幂等处理,失败抛异常
    }
    consumer.commitSync();                // 全部成功后再提交
}

两个反例:commitSync 放在循环外但在处理之前(先提交后处理);用 enable.auto.commit=true 却指望"处理失败不丢"。自动化提交由后台线程按时间触发,与业务处理结果毫无关系。

四、验证与演练

检查项命令/方法期望结果
副本与 ISRkafka-topics.sh --describeISR 数量 = 副本数,无 -1
消费积压kafka-consumer-groups.sh --describeLAG 稳定,不持续增长
副本一致性kafka-replica-verification.sh无最大滞后告警
断电演练直接 kill -9 一个 Broker生产端无 NotEnoughReplicas 之外的失败,数据可读
脏选举手动停两个副本再写入应抛 NotEnoughReplicasException 而不是成功

五、常见追问

  • acks=all 就落盘了吗? 没有。Broker 写入页缓存即返回,靠的是多副本冗余而不是 fsync;真要防整机掉电,得配 log.flush.interval.messages=1,代价是吞吐骤降。
  • 副本数 3 就够了吗? 还需 min.insync.replicas=2,否则 ISR 缩到 1 时 acks=all 退化成 acks=1,写入照常成功但只有一份数据。
  • 为什么说 Kafka 不能 100% 不丢? 见下一章:涉及页缓存、跨机房、消费者进程崩溃等边界,工程上只能做到"可度量、可补偿"。
  • 重平衡会丢消息吗? 会丢"已拉取未提交"的消息(如果先提交后处理),也会重复投递(处理完成未提交),本质仍是提交时机问题。

小结

  • 不丢是三段配置的乘积,且默认值在可靠性上普遍偏弱,必须显式声明。
  • acks=all + min.insync.replicas=2 + 副本 3 + 禁止脏选举,是 Broker 侧的底线组合。
  • 消费端只认"处理成功再提交",自动提交与顺序颠倒都会丢。
  • 用 describe、lag、kill -9 演练来验证,而不是只看配置文件。
笔记加载中…