面试知识库
极高 进阶

消息丢失与可靠性保证#

一句话答案#

三环节保证不丢:生产端(acks=all + 重试)→ Broker(多副本 ISR + min.insync.replicas)→ 消费端(手动提交 offset + 幂等处理)。任何一环断裂都会丢消息。

核心要点

全链路分析#

Producer ──发送──→ Broker ──存储──→ Consumer
   │                │                 │
   ├── 网络丢失     ├── 宕机丢失      ├── 自动提交后处理失败
   ├── 超时未重试   ├── 副本不足      └── offset 跳过
   └── 序列化失败   └── 刷盘前崩溃
plaintext

第一环:Producer → Broker#

acks 参数详解:

acks含义性能可靠性
0不等待任何确认最高最低(发完就忘)
1等待 Leader 确认中等中等(Leader 宕机可能丢)
all(-1)等待 ISR 中所有副本确认最低最高

acks=all 的陷阱:

如果 ISR 中只有 Leader 一个副本(其他副本被踢出 ISR)
→ acks=all 效果等于 acks=1
→ Leader 宕机仍会丢消息

解决:配合 min.insync.replicas=2
→ ISR 副本数 < 2 时,Producer 直接报错拒绝写入
→ 用可用性换可靠性
plaintext

推荐配置:

# Producer 端
acks = all
retries = 3                          # 发送失败重试
retry.backoff.ms = 100               # 重试间隔
max.in.flight.requests.per.connection = 1   # 保证重试时消息有序(防止乱序)

# Broker 端
min.insync.replicas = 2              # ISR 至少 2 个副本
replication.factor = 3               # 每个 Partition 3 个副本
properties

第二环:Broker 存储#

Kafka 消息写入流程:

Producer → Leader Broker (写入 Page Cache) → 操作系统异步刷盘

                 Follower 从 Leader 拉取复制
plaintext

可能丢失的点:

  • Page Cache 未刷盘时 Broker 宕机 → 配合副本机制,其他副本有数据
  • 所有 ISR 副本同时宕机 → 极端情况,概率极低

不建议用 flush.messages=1(每条消息同步刷盘):

  • 性能下降 10 倍以上
  • Kafka 的设计哲学是用副本保可靠性,而不是用刷盘

第三环:Consumer 消费#

自动提交 offset 的坑:

Consumer 拉取消息 → 自动提交 offset → 处理消息失败
→ offset 已提交,消息不会再投递 → 消息"丢失"(实际是跳过了)
plaintext

正确做法:手动提交

consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord record : records) {
    // 1. 先处理业务逻辑
    processMessage(record);
    // 2. 处理成功后再提交 offset
    consumer.commitSync();
}
java

手动提交的粒度选择:

方式特点
每条提交最安全,但性能差(频繁 IO)
每批提交性能好,但失败时整批重试(需要幂等)
异步提交性能最好,但提交失败不会阻塞(需要回调处理)

推荐: 每批处理完同步提交 + 消费端做幂等处理。

消费端幂等#

手动提交保证不跳过消息,但可能重复消费(处理完了但提交 offset 前宕机)。

幂等方案:

  • 数据库唯一键(相同消息 ID 插入报唯一冲突,忽略)
  • Redis SET NX(消息 ID 做 Key,消费前检查是否存在)
  • 业务状态机(订单已支付 → 重复支付消息忽略)

完整的可靠性配置清单#

Producer:
  ✅ acks = all
  ✅ retries = 3+
  ✅ enable.idempotence = true(防止重试导致的重复消息)

Broker:
  ✅ replication.factor = 3
  ✅ min.insync.replicas = 2
  ✅ unclean.leader.election.enable = false(不允许非 ISR 副本成为 Leader)

Consumer:
  ✅ enable.auto.commit = false
  ✅ 手动 commitSync / commitAsync + callback
  ✅ 消费逻辑做幂等
plaintext
面试回答(2分钟版)

消息不丢需要三环节同时保证。Producer 端:acks=all 确保 ISR 所有副本确认,配合 retries 自动重试网络瞬时故障。但 acks=all 有个陷阱——如果 ISR 只有 Leader 一个副本,效果等于 acks=1,所以 Broker 端必须配 min.insync.replicas=2,ISR 不足时直接拒写。Broker 端还要配 replication.factor=3、unclean.leader.election=false 防止非 ISR 副本当 Leader 导致数据丢失。Consumer 端关掉自动提交,处理完业务逻辑再手动 commitSync。但手动提交可能导致重复消费(处理完了没来得及 commit 就宕机),所以消费端必须做幂等——用数据库唯一键或 Redis SETNX。三环节加起来是 At-Least-Once + 业务幂等,这是大多数生产系统的选择。

追问与易错

追问方向:

  • “acks=all 影响性能多少?”→ 延迟增加(等待副本确认),吞吐量下降 20%-40%,但可通过 batch 和异步发送缓解
  • “消费者宕机未 ACK 怎么办?”→ 消息会重新投递给同组其他消费者,需要幂等
  • “min.insync.replicas 设太大会怎样?”→ 可用性降低,副本不够时写入全部拒绝

易错点:

  • ❌ “acks=all 就绝对不丢”——ISR 只有 Leader 时等于 acks=1,必须配 min.insync.replicas
  • ❌ “消费完就 commit offset”——应该处理完业务逻辑再 commit
  • ❌ “Kafka 不丢消息”——任何中间件都可能丢,关键是配置正确 + 业务兜底