考察点
这题表面问一个参数,实际考你对整条数据链路的可靠性有没有系统认识。只想背"acks=all"的人会在追问里露馅:min.insync.replicas 是什么?Broker 落盘了吗?消费端位移提交时机对不对?面试官想看到你能把"不丢"拆成生产、Broker、消费三段分别给方案,并且知道每段的代价。
参考答案
acks 三档语义
acks 是 Producer 端参数,决定"一条消息算发送成功"需要多少个副本确认:
| 取值 | 语义 | 吞吐 | 风险 |
|---|---|---|---|
| 0 | 发出去就不管,不等任何确认 | 最高 | Leader 挂了消息直接丢,发送方无感知 |
| 1 | 等 Leader 写入本地日志就返回 | 高 | Leader 写完、Follower 还没同步时 Leader 宕机,这条消息丢 |
| all(-1) | 等 ISR 全部副本确认 | 较低 | ISR 收缩到只剩 Leader 时退化为 acks=1 |
acks=1 的丢数据场景要讲清楚:Leader 落盘返回成功,Follower 还没拉走这条消息,此时 Leader 宕机,某个 Follower 当选新 Leader,那条消息就没了。Producer 侧看到的明明是"发送成功",这就是很多人误以为自己配了 acks=1 就安全的地方。
acks=all 也有坑:如果 ISR 只剩 Leader 一个(其他 Follower 全被踢出去了),all 实际只等 Leader。所以必须搭配 Broker 端的 min.insync.replicas=2,意思是 ISR 不足 2 个副本时,Broker 直接拒绝写入,Producer 收到 NotEnoughReplicas 异常。宁可写不进去报错,也不静默降级成单副本写入——这是用可用性换可靠性的经典取舍,副本数至少配 3 才有意义。
生产端不丢的完整姿势
最小配置组合是:
acks=all- Broker 侧
min.insync.replicas=2,Topic 副本数replication.factor=3 retries调大(新版本默认 Integer.MAX_VALUE),enable.idempotence=true(新默认开启),避免重试导致乱序或重复delivery.timeout.ms控制整体超时,别让重试无上限地占着发送配额
代码层面,send() 返回的是 Future,异步发送必须处理回调里的异常;同步 send().get() 才能拿到最终确认。只调 send 不看结果,等于 acks 白配。
还有一个常被忽略的点:max.in.flight.requests.per.connection。不开幂等时,如果它大于 1 且发生重试,可能出现消息乱序——第一批失败重试,第二批先成功了。开幂等后这个值可以放到 5,Kafka 用序列号保证顺序。
Broker 端:写进 page cache 不等于落盘
ISR 确认只保证消息到了多个副本的内存(page cache),不保证刷盘。flush.messages、flush.ms 这两个参数官方都不建议配,因为 Kafka 的设计哲学是:靠多副本冗余扛单机宕机,而不是靠单机刷盘扛断电。真要扛机房级断电,那是跨机房容灾的话题,不是 flush 参数能解决的。副本数够、ISR 健康,单点宕机丢不了数据,这个信任链条要说出来。
另外要监控 ISR 的抖动:如果某个分区的 ISR 频繁伸缩(Broker 端有对应的 UnderReplicatedPartitions 和 ISR shrink/expand 指标),说明 Follower 在反复掉队,此刻即使 acks=all,实际参与确认的副本数可能经常贴着 min.insync.replicas 的下限走,可靠性水位比配置看上去要低,这种情况该去查慢盘或网络,而不是继续加参数。
消费端:不丢的最后一段
Producer 和 Broker 配得再好,消费端位移提交错了照样"丢"——消息还没处理完,offset 先提交了,Consumer 崩溃重启后从已提交的位移继续,那条消息就被跳过了。正确姿势是处理完再提交位移,自动提交(enable.auto.commit=true)在消费慢、处理链路长的场景就是定时炸弹,改手动提交,或者用事务/原子写把"处理结果"和"位移"绑在一起。
一句话收拢:不丢 = 生产端 acks=all + 幂等重试,Broker 端 3 副本 + min.insync.replicas=2,消费端先处理后提交。三段缺一段,"不丢"都不成立。
可能的追问
- acks=all + min.insync.replicas=2 就一定能扛住吗?什么场景还丢? ISR 里两个副本所在机器同时挂、或者开了 unclean leader election 时仍可能丢;另外配置了 retention 的消息被清理属于"正常过期",不算丢但业务上要想清楚。
- acks=all 吞吐掉多少,怎么补回来? 等待副本确认会增加单条延迟,靠 Producer 的 batching(
linger.ms+batch.size)和多分区并行把吞吐补回来,延迟敏感的分区可以单独评估。 - 为什么 retries 要配合幂等? 单纯重试在网络抖动时可能让同一条消息被写入两次(Broker 已写入但 ACK 丢了),幂等生产者用 PID+序列号去重,重试才真正安全。