ACK 与一致性保证
定义与作用
ACK(Acknowledgment)是 Producer 配置项 acks,决定 Producer 等待多少个副本确认后才认为消息发送成功。ACK 机制+min.insync.replicas 共同构成了 Kafka 的写入一致性模型。
acks 参数是 Kafka 在可靠性与延迟之间的核心权衡杠杆。
核心原理
三种 ACK 级别
ACK 与 min.insync.replicas 的协同
acks | min.insync.replicas | 行为 |
|---|---|---|
| 0 | — | 发送即成功,不等待 |
| 1 | — | Leader 写入即成功 |
| all | 1 | Leader 写入即成功(acks=all 退化为 acks=1) |
| all | 2 | Leader + 至少 1 个 Follower 确认 |
| all | 3 | Leader + 至少 2 个 Follower 确认 |
一致性语义
| 配置组合 | 一致性语义 | 数据丢失风险 |
|---|---|---|
acks=0 | 无保证 | 高(网络/Leader 故障都丢) |
acks=1 | At-most-once | 中(Leader 故障但未同步到 Follower 时丢) |
acks=all, min.insync.replicas=1 | At-least-once | 低(但极端情况仍可能丢) |
acks=all, min.insync.replicas=2, RF=3 | 强一致性 | 极低(需 2 个 ISR 同时故障) |
完整示例
示例一:对比三种 ACK 级别的性能与可靠性
场景:3 副本 Topic,分别用三种 ACK 级别发送 10000 条消息。
// 配置
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "...");
props.put("value.serializer", "...");
// 实验 1:acks=0
props.put("acks", "0");
// 测试发送 10000 条...
结果对比:
# 实验 1: acks=0
# 吞吐: ~500K msg/s, 延迟 P99: <1ms
# 模拟 Leader 故障: 丢失 ~300 条(未持久化)
# 实验 2: acks=1
# 吞吐: ~250K msg/s, 延迟 P99: ~3ms
# 模拟 Leader 故障: 丢失 ~50 条(未同步到 Follower)
# 实验 3: acks=all (min.insync.replicas=2)
# 吞吐: ~150K msg/s, 延迟 P99: ~10ms
# 模拟 Leader 故障: 丢失 0 条
示例二:模拟 Leader 故障时 acks=1 丢失数据
# 终端 1:Producer(acks=1)
bin/kafka-console-producer.sh --topic demo-ack1 --bootstrap-server localhost:9092 \
--producer-property acks=1
> msg1
> msg2
> msg3
> msg4
> msg5
# ... (持续发送中)
# 终端 2:在发送过程中杀 Leader
# 假设 Partition 0 Leader = Broker 1
kill -9 $(jps | grep "server1" | awk '{print $1}')
# 结果:在 kill 的时刻,Leader 刚写入但未同步到 Follower 的消息丢失
# Producer 收到 NOT_LEADER_FOR_PARTITION,重试成功(连到新 Leader)
示例三:acks=all + min.insync.replicas=2 的安全写入
# 创建安全 Topic
bin/kafka-topics.sh --create --topic safe-orders \
--partitions 3 --replication-factor 3 \
--config min.insync.replicas=2 \
--bootstrap-server localhost:9092
# Producer(acks=all)
bin/kafka-console-producer.sh --topic safe-orders --bootstrap-server localhost:9092 \
--producer-property acks=all
> order-001 # 成功(ISR=[1,2,3] ≥ 2)
> order-002 # 成功
易错场景
易错 1:acks=all 但 min.insync.replicas=1
这是最常见的配置陷阱。min.insync.replicas 默认值为 1,仅设置 acks=all 而不改 min.insync.replicas 时,acks=all 等价于 acks=1——Leader 确认即返回,不等待任何 Follower。
易错 2:追求"绝对不丢"设 min.insync.replicas = replication-factor
后果:任何一个 Follower 出问题(哪怕 1 秒的网络抖动)→ ISR < min.insync.replicas → 写入全部拒绝 → 服务不可用。
最佳实践:min.insync.replicas = replication-factor - 1(如 RF=3, minISR=2),既保证可靠性又保证可用性。
面试高频考点
Q:acks=all 是否保证绝对不丢数据?
A:不保证。acks=all 保证的是:消息被所有 ISR 副本确认后才返回成功。但以下情况仍可能丢失:
- ISR 全部永久故障(如磁盘损坏):所有副本数据丢失 → 除非有异地备份
min.insync.replicas=1时的acks=all:等价于acks=1,Leader 故障可能丢unclean.leader.election.enable=true:ISR 全挂后选非 ISR 副本,缺失同步数据- Broker 磁盘损坏:即使写入确认,物理介质故障仍可导致丢失
Kafka 的可靠性是分布式级别的(容忍节点故障),不是容灾级别的(跨机房/区域)。需要容灾级别需要 MirrorMaker 2 等工具实现跨集群复制。