订阅更新

Kafka 如何保证消息不丢失?

面试问 013 约 4 分钟读完 4 次阅读

本文目录
  1. 面试怎么回答
  2. 第一段:生产端必须知道消息到底发成功没有
  3. 第二段:Broker 要有可用副本,而不只是“副本数大于 1”
  4. 第三段:消费端不要在业务完成前提交 Offset
  5. 第四段:没有监控和补偿,就不能声称“不会丢”
  6. 一个典型场景

面试怎么回答

我会从生产端、Broker、消费端和监控补偿四层回答。生产端使用 acks=all、合理重试和幂等生产,并处理最终发送失败;Broker 侧设置合适的副本因子和 min.insync.replicas,持续监控 ISR、复制延迟和磁盘;消费端必须在业务处理成功后再提交 Offset,并依靠下游幂等或事务处理重复消费;最后通过失败重试、死信、Lag 监控和端到端对账形成闭环。需要强调的是,单个参数只能保证链路的一段,不能直接等价为业务端到端不丢且不重。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

很多人会直接回答:

“设置 acks=all,增加副本数,就不会丢消息。”

这只覆盖了消息写入 Broker 的一段链路。

一条消息从业务代码产生,到 Kafka 持久化,再到消费者处理并写入下游,任何一段提前确认、异常未处理或状态提交错误,都可能形成业务上的数据丢失。

所以更准确的回答应该是:

Kafka 消息不丢失,需要生产端、Broker、消费端和监控补偿四段一起闭环。


第一段:生产端必须知道消息到底发成功没有

生产端不能调用一次 send 就默认成功。

需要正确处理异步发送结果和最终失败:

  • 使用 acks=all,等待所有同步副本确认
  • 对可重试异常启用合理重试
  • 开启幂等生产,避免重试造成同一分区内重复写入
  • 处理发送回调或 Future 中的最终异常
  • 序列化失败、消息过大等不可重试错误进入告警或补偿链路

acks=all 提升的是 Broker 确认前的持久性,但它不能替应用处理最终发送失败。

如果业务代码捕获异常后直接丢弃,Kafka 配置再可靠也没有用。


第二段:Broker 要有可用副本,而不只是“副本数大于 1”

Broker 侧要同时看:

  • Topic 的副本因子
  • 同步副本集合 ISR 是否健康
  • min.insync.replicas 是否符合可靠性要求
  • Broker 故障时是否仍有足够同步副本
  • 磁盘、复制延迟和不可用分区是否被监控

生产端使用 acks=all 时,Leader 会等待 ISR 中的副本确认。配合合理的最小同步副本数,可以避免只剩一个脆弱副本时仍继续接受高可靠写入。

但可靠性和可用性存在取舍:同步副本不足时拒绝写入,会保护数据,却可能让业务暂时不可写。

这不是配置错误,而是可靠性策略的边界。


第三段:消费端不要在业务完成前提交 Offset

消费端最常见的“丢数据”,其实不是 Kafka 里没有消息,而是 Offset 已经前进,但业务处理没有成功。

例如消费者先提交 Offset,再写数据库。提交成功后数据库写入失败,任务重启会从新 Offset 继续,这条消息就不会再处理。

更安全的顺序是:

读取消息 → 完成业务处理或持久化 → 再提交对应 Offset。

这样失败后可能重复消费,但不会因为提前提交而跳过消息。

因此消费端通常要配合:

  • 手动或受控提交 Offset
  • 下游幂等键、去重或事务
  • 明确的失败重试与死信处理
  • 批量消费时确认提交边界

Kafka 消息可靠性链路

如果要求 Kafka 到下游的 Exactly-Once,还需要消费位点与下游写入能够进入同一事务语义,或让下游基于业务键可靠幂等。仅开启生产者幂等,并不等于整个业务链路 Exactly-Once。


第四段:没有监控和补偿,就不能声称“不会丢”

分布式系统无法靠一组参数消除所有故障。

生产上至少要监控:

  • Producer 发送失败和重试
  • ISR 缩减、Under Replicated Partition
  • Broker 磁盘与复制延迟
  • Consumer Lag、消费异常和提交失败
  • 死信数量与补偿积压

关键链路还应该做源端、Kafka 和下游之间的数量或业务主键对账。

监控负责发现,重试和补偿负责恢复,对账负责证明链路最终完整。


一个典型场景

比如消费者读取订单消息后,先自动提交 Offset,再调用下游接口。

大部分时间任务正常,所以监控只关注 Consumer Lag,看起来没有任何积压。

某次下游接口超时,消费逻辑失败,但 Offset 已经提交。任务恢复后不会重新读取这条消息,Lag 仍然是零,订单却没有进入下游。

这个问题不会通过增加 Kafka 副本解决,因为消息在 Kafka 中原本是完整的。

真正需要修改的是提交顺序、下游幂等和失败补偿,并增加业务结果对账。


刷题进度保存在当前浏览器