Kafka 丢消息可能发生在哪?怎么保证kafka无消息丢失?

1 阅读3分钟

对于这个问题我个人的见解是:

首先,我们要搞清楚kafka保证消息不丢失的职责边界;

一句话概括,Kafka 只对已提交的消息做有限度的持久化保证。

第一个核心要素是“已提交的消息”。什么是已提交的消息?当 Kafka 的若干个 Broker 成功地接收到一条消息并写入到日志文件后,它们会告诉生产者程序这条消息已成功提交。此时,这条消息在 Kafka 看来就正式变为“已提交”消息了。

第二个核心要素就是“有限度的持久化保证”,也就是说 Kafka 不可能保证在任何情况下都做到不丢失消息。举个极端点的例子,如果地球都不存在了,Kafka 还能保存任何消息吗?显然不能!倘若这种情况下你依然还想要 Kafka 不丢消息,那么只能在别的星球部署 Kafka Broker 服务器了。

从这三方面入手:Producer → Broker → Consumer

Kafka 的无消息丢失不是单点配置,而是 Producer、Broker、Consumer 三端协同的结果。

Producer 端通过 acks=all 和幂等性确保消息成功写入多个副本;Broker 端通过副本机制、最小 ISR 和禁止不干净选举确保数据不丢;Consumer 端通过手动提交 Offset,确保消息被成功处理后才确认消费。
通过这套配置,可以在工程上实现高可靠的“无消息丢失”。

Producer 端:保证“消息一定成功写入 Kafka”

1️⃣ 必须开启 ACK 确认机制(核心)

acks=all

含义:

  • Leader 写入成功
  • 所有 ISR 副本写入成功
  • Producer 才认为发送成功

❌ 如果用 acks=1 / 0
👉 Broker 宕机时直接丢消息

2️⃣ 开启重试(防网络抖动)

retries=Integer.MAX_VALUE
delivery.timeout.ms=120000

防止:

  • 短暂网络失败
  • Leader 切换

3️⃣ 开启幂等性(防重复 + 顺序)

enable.idempotence=true

作用:

  • Producer 重试不会产生重复消息
  • 保证单 Partition 内顺序

👉 这是无丢失配置的“必选项”

Broker 端:保证“消息不会写进去就没了”

1️⃣ 副本数 ≥ 2(生产通常 3)

replication.factor=3

原因:

  • 1 副本 = 单点
  • Broker 挂了直接丢

2️⃣ ISR 至少 2 个副本确认

min.insync.replicas=2

3️⃣ 禁止“不干净选举”(非常关键)

unclean.leader.election.enable=false

解释:

它控制的是哪些 Broker 有资格竞选分区的 Leader。如果一个 Broker 落后原先的 Leader 太多,那么它一旦成为新的 Leader,必然会造成消息的丢失。故一般都要将该参数设置成 false,即不允许这种情况的发生。

否则:

  • 可能选一个没有完整数据的副本
  • 历史消息直接丢失

Consumer 端:保证“消息一定被正确消费”

1️⃣ 关闭自动提交 Offset(重中之重)

enable.auto.commit=false

解释:书签的例子,先看完内容再更新书签,这里书签就好比Offset;这里除了把这个参数设置成false之外,并采用手动提交位移的方式。

否则:

  • 先提交 Offset
  • 再处理消息
  • 程序崩了 → 消息永久丢失

2️⃣ 手动提交 Offset(在处理成功后)

process(record);
consumer.commitSync();

语义:

处理成功 → 才告诉 Kafka 我消费过了

总结:

## Producer 端标准配置:
acks=all
enable.idempotence=true
retries=Integer.MAX_VALUE


## Broker 端标准配置:
replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false

## Consumer 端标准配置:
enable.auto.commit=false
组件配置目的
Produceracks=all写入确认
Producerenable.idempotence=true防重复
Brokerreplication.factor ≥ 3副本冗余
Brokermin.insync.replicas ≥ 2写入安全
Brokerunclean.leader.election=false不丢历史
Consumerenable.auto.commit=false防提前提交

踩坑:

acks=all 就一定安全吗?

❌ 不够:

  • ISR=1 时仍然危险
  • 必须配合 min.insync.replicas