Kafka 如何保证消息不丢失?
本文目录
面试怎么回答
我会从生产端、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 到下游的 Exactly-Once,还需要消费位点与下游写入能够进入同一事务语义,或让下游基于业务键可靠幂等。仅开启生产者幂等,并不等于整个业务链路 Exactly-Once。
第四段:没有监控和补偿,就不能声称“不会丢”
分布式系统无法靠一组参数消除所有故障。
生产上至少要监控:
- Producer 发送失败和重试
- ISR 缩减、Under Replicated Partition
- Broker 磁盘与复制延迟
- Consumer Lag、消费异常和提交失败
- 死信数量与补偿积压
关键链路还应该做源端、Kafka 和下游之间的数量或业务主键对账。
监控负责发现,重试和补偿负责恢复,对账负责证明链路最终完整。
一个典型场景
比如消费者读取订单消息后,先自动提交 Offset,再调用下游接口。
大部分时间任务正常,所以监控只关注 Consumer Lag,看起来没有任何积压。
某次下游接口超时,消费逻辑失败,但 Offset 已经提交。任务恢复后不会重新读取这条消息,Lag 仍然是零,订单却没有进入下游。
这个问题不会通过增加 Kafka 副本解决,因为消息在 Kafka 中原本是完整的。
真正需要修改的是提交顺序、下游幂等和失败补偿,并增加业务结果对账。