消息确认机制(acks)深度剖析
消息确认机制,说白了就是生产者跟 Broker 之间的一种「握手协议」。你发一条消息过去,到底要不要等对方回一句「收到了」?等多久?等几个人回?这些就是 acks 参数控制的事情。
我个人习惯把 acks 理解为「可靠性的三个档位」。你调得越高,数据越安全,但延迟也越大。调低了,吞吐量上去了,但丢数据的风险也跟着上去了。怎么选?得看你的业务场景。
核心结论:acks 参数决定了生产者等待多少个副本确认消息写入成功,才认为这条消息发送完成。
acks=0:只管发,不管死活
这个模式下,生产者把消息扔给网络就完事了。它不关心 Broker 有没有收到,也不管有没有写入成功。说白了就是「发完即忘」。
优点:延迟最低,吞吐量最高。
缺点:丢数据风险最大。网络闪断、Broker 宕机、磁盘写满……任何环节出问题,消息就丢了,生产者完全不知道。
我曾经在一个日志采集系统里见过这种配置。业务方说「日志丢几条无所谓,别影响主流程」。嗯,这种场景用 acks=0 确实合适。但你要是做交易、做支付,千万别碰这个。
避坑指南:我曾经见过有人把 acks=0 用在订单系统里,结果某次网络抖动丢了上千条订单消息,排查了一整天才发现是 acks 配置的问题。从那以后,我对 acks=0 的使用场景变得非常谨慎。
acks=1:Leader 确认即可
这是 Kafka 的默认配置。生产者发送消息后,等待 Leader 副本写入成功并返回确认,就算完事。Follower 副本有没有同步?不管。
这个模式是「吞吐量与可靠性」的折中方案。大多数场景下够用了。但你要知道,如果 Leader 刚确认完就挂了,而 Follower 还没来得及同步,这条消息就丢了。
我记得有一次线上事故,就是 acks=1 配合单副本 Topic 出的问题。Leader 挂了,消息全丢,恢复后发现数据少了十几分钟。后来我强制要求所有核心业务 Topic 至少 3 个副本,acks 必须配成 all。
| 配置 | 确认时机 | 丢数据风险 | 延迟 | 适用场景 |
|---|---|---|---|---|
| acks=0 | 不等待确认 | 极高 | 最低 | 日志、监控、可丢数据 |
| acks=1 | Leader 写入完成 | 中等 | 中等 | 大部分业务场景 |
| acks=-1 (all) | 所有 ISR 副本写入完成 | 极低 | 最高 | 金融、交易、核心数据 |
acks=-1(all):等所有人都点头
这个模式下,生产者要等 Leader 和所有 ISR(In-Sync Replicas)列表里的 Follower 副本都写入成功,才返回确认。你想想看,这意味着什么?
意味着只要 ISR 里还有一个副本活着,你的数据就不会丢。这是 Kafka 能提供的最强可靠性保证。
但代价也很明显:延迟变高了。如果某个 Follower 副本响应慢,整个生产者的发送都会被拖慢。我见过有人把 acks=all 配上去后,吞吐量直接掉了 30%。
我的建议:核心业务用 acks=all,但一定要配合合理的副本数和 min.insync.replicas 配置。别盲目追求「最可靠」,否则性能崩了谁也救不了。
min.insync.replicas:协同配置的关键
光配 acks=all 还不够。你想想,如果 ISR 里只剩一个 Leader 副本了,那 acks=all 跟 acks=1 有什么区别?没区别。
min.insync.replicas 就是用来解决这个问题的。它定义了「最少需要多少个同步副本」,生产者才能继续写入。如果 ISR 里的副本数低于这个值,Broker 会直接拒绝写入,返回异常。
举个例子:Topic 配置了 3 个副本,min.insync.replicas=2。如果某个 Follower 挂了,ISR 里还剩 Leader + 1 个 Follower,共 2 个,满足条件,可以继续写。如果又挂了一个,ISR 只剩 Leader 自己了,这时候再写入就会报错。
# Topic 级别配置示例
# 创建 Topic 时指定
bin/kafka-topics.sh --create \
--topic my-topic \
--partitions 3 \
--replication-factor 3 \
--config min.insync.replicas=2
# Broker 级别默认配置(在 server.properties 中)
min.insync.replicas=1
重要提醒:min.insync.replicas 必须小于或等于 replication-factor。如果设成 3,但副本数只有 2,那永远写不进去。我曾经见过有人把这两个参数配反了,排查了半天才发现是配置冲突。
acks 与 min.insync.replicas 的协同逻辑
这两个参数是配合使用的。我画了一张图,帮你理清它们的关系:
实际配置建议
说了这么多理论,到底怎么配?我直接给几组常用的组合:
- 最高可靠性(金融、交易):acks=all,replication-factor=3,min.insync.replicas=2。这样即使挂一个 Broker,数据依然安全。
- 平衡型(大部分业务):acks=1,replication-factor=2,min.insync.replicas=1。吞吐量和可靠性都还行。
- 极致吞吐(日志、监控):acks=0,replication-factor=1,min.insync.replicas=1。丢了就丢了,别影响性能。
一个小技巧:我习惯在生产者端也配一个重试机制。即使 acks=all 配合 min.insync.replicas=2,也可能因为网络抖动导致写入失败。配上 retries=3 和 retry.backoff.ms=100,能有效提高写入成功率。
// Java 生产者配置示例
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "all");
props.put("min.insync.replicas", "2");
props.put("retries", 3);
props.put("retry.backoff.ms", 100);
props.put("linger.ms", 5);
props.put("batch.size", 16384);
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
嗯,到这里 acks 和 min.insync.replicas 的协同配置就讲完了。你记住一句话:acks 决定「等谁确认」,min.insync.replicas 决定「最少等几个」。两者缺一不可。