广告归因数据丢了一条,ROI算错了——Producer的acks到底怎么配?
先说结论
acks参数决定了Producer认为消息'发送成功'的条件:acks=0(发了就忘,最快最危险)、acks=1(Leader写入即成功,默认值)、acks=all(ISR全部副本确认,最安全)。
实际生产中不同业务用不同配置:广告归因/交易数据用acks=all + min.insync.replicas=2;用户行为埋点/日志用acks=1够了;acks=0几乎不用。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
在某短剧出海公司,我负责投放中台的数据处理,提供每5分钟的数据时效,便于投手进行广告投放决策。广告归因数据每一条都直接影响ROI计算——如果某个广告平台回传的一条归因丢了,这个用户的获客成本就算错了,投手可能做出错误决策。这就是为什么Kafka的消息可靠性不是纸上谈兵,而是真金白银的问题。acks参数就是这道防线的核心。
三种acks模式详解
acks=0:发完就忘(Fire and Forget)
Producer发出消息后不等任何确认。Broker宕机或网络抖动,消息丢了Producer完全不知道。唯一优点是吞吐量最高。适用场景几乎没有,除非做压测。
acks=1:Leader确认(默认值)
Producer等Leader Broker写入本地Log就算成功。如果Leader写完后、Follower同步前Leader挂了,消息就丢了。概率很低但确实存在。对用户行为埋点,偶尔丢一两条不影响大盘统计,用acks=1就够了。
acks=all(或acks=-1):ISR全部确认
Producer等所有ISR副本都写入成功才算成功。最安全,但要配合min.insync.replicas用,否则ISR只剩Leader一个也算all,和acks=1没区别。
// Producer配置示例
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092,broker3:9092");
// 方案一:高吞吐(埋点/日志场景)
props.put("acks", "1");
props.put("retries", 3); // 失败重试3次
props.put("batch.size", 32768); // 32KB批量发送
props.put("linger.ms", 5); // 等5ms攒一批再发
// 方案二:高可靠(交易/归因场景,我在短剧公司广告归因用这个)
props.put("acks", "all");
props.put("retries", Integer.MAX_VALUE); // 无限重试
props.put("enable.idempotence", true); // 幂等,防重试产生重复
props.put("max.in.flight.requests.per.connection", 5); // 幂等模式最多5
acks=all的隐藏陷阱:min.insync.replicas
很多人以为设了acks=all就万事大吉,其实不是。acks=all的含义是'所有ISR副本确认'。如果ISR只剩Leader一个(其他Follower都掉队了),acks=all等价于acks=1,该丢还是丢。
所以必须配合min.insync.replicas使用:
# Broker端配置(server.properties 或 Topic级别覆盖)
min.insync.replicas=2
# 推荐组合(3副本集群):
# replication.factor=3 ← 每个Partition 3个副本
# min.insync.replicas=2 ← 至少2个ISR副本确认
# acks=all ← Producer等所有ISR确认
#
# 效果:1个Broker挂了 → 剩2个ISR → 正常写入
# 2个Broker挂了 → ISR不足2个 → Producer报错(不丢数据但停写)
# 在停写和丢数据之间,我们选停写——数据补得回来,丢了就真没了
三种acks性能对比
| 维度 | acks=0 | acks=1 | acks=all |
|---|---|---|---|
| 可靠性 | 最低,可能丢 | 中等,Leader挂会丢 | 最高,ISR全确认 |
| 吞吐量 | 最高 | 高 | 较低(等多副本确认) |
| 延迟 | 最低(<1ms) | 低(~5ms) | 较高(~10-20ms) |
| 我的使用场景 | 没用过 | 埋点/行为日志 | 广告归因/虚拟礼物交易 |
| 搭配参数 | 无 | retries=3 | min.insync.replicas=2+幂等 |
实战经验:不同公司的不同选择
在某元宇宙社交公司:用户行为埋点(点击、浏览、在线时长)用acks=1,偶尔丢一两条不影响大盘统计。但虚拟礼物交易Topic用acks=all + min.insync.replicas=2,丢一条交易就会导致财务对不上账。
在某短剧出海公司:广告归因数据用acks=all。每条归因直接影响ROI计算——某广告平台回传说某用户是通过广告A获取的,如果丢了,广告A的ROI就偏低,投手可能误判效果不好而停掉它。归因数据宁可慢一点也不能丢。
在某知名出海硬件品牌:埋点SDK采集的数据经Kafka流入Databricks,用acks=1。将1000+埋点事件聚合为100+标准事件后,单条丢失对整体分析影响极小。
面试高频追问
追问一:开了acks=all消息就一定不丢吗?
不一定。三个漏洞:① 没配min.insync.replicas,ISR退化到只剩Leader;② Broker磁盘坏了,数据还在Page Cache没刷盘;③ Producer端发送前就失败了(比如序列化异常),消息根本没进Kafka。端到端不丢还需要Producer的回调确认+Consumer的手动提交offset。
追问二:acks=all会不会很慢?
比acks=1慢,但没想象中夸张。3副本集群大约多10-15ms延迟。可以增大batch.size和linger.ms来提吞吐。我在短剧公司的归因链路,acks=all配合linger.ms=10ms,端到端5分钟时效完全没问题。
追问三:enable.idempotence是什么?
开启Producer幂等性。retries>0时,网络抖动可能导致同一条消息发两次。幂等模式下Kafka给每条消息一个序列号,Broker端自动去重。推荐acks=all时一起开启。
总结
acks参数是Kafka可靠性的第一道防线。面试时记住万能公式:acks=all + min.insync.replicas=2 + replication.factor=3 = 金融级可靠性。然后根据业务场景说清楚'为什么埋点用acks=1、交易用acks=all',面试官会觉得你对可靠性有分层认知,而不是无脑all。
下一篇聊Consumer端的问题:Flink消费Kafka突然全停了,Rebalance到底怎么回事?
你们核心Topic的acks配的啥?有没有因为acks配错丢过数据?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长