Kafka 为什么不用 Raft协议?

📅 2026/6/27 3:55:12
Kafka 为什么不用 Raft协议?
一句话回答Kafka 不是一个队列需要强一致而是一个日志流需要高吞吐。Raft 是为强一致设计的强约束协议跟 Kafka 的基因高吞吐、水平扩展、回溯消费天然冲突。Kafka 用了类 Quorum 思想但更松散的 ISR 机制——这是它在 CAP 里选了 AP 之后的工程妥协。一、先理解Raft 的本质是什么Raft 是一个强一致 严格约束的协议Raft 的硬性要求1. 任何写入必须被多数派Quorum确认才返回2. Leader 必须串行处理所有写请求不能并行3. 每次 Leader 变更都要 Term 自增4. Follower 只能从当前 Leader 同步拒绝旧 Leader5. 提交commit必须按日志顺序6. 任何不一致都会被检测和修复这套机制换来的强一致 不会丢数据 不会脑裂。代价每次写都要等多数派网络往返——延迟是硬伤。二、Kafka 不用 Raft 的 6 个核心理由1.架构基因不同一个队列 vs 一个日志流RabbitMQ Quorum Queue一个队列就是一份数据强一致是核心诉求消息不能丢、不能重Raft 的每次写等多数派在低吞吐下完全可接受Kafka一个 Topic 有几百个 Partition每个 Partition 独立核心诉求是吞吐百万级/s如果每个 Partition 都走 Raft 多数派同步 → 延迟直接爆炸类比RabbitMQ 是市区公路强一致 保证畅通 Kafka 是高速公路高吞吐 千万辆车/小时 Raft 是红绿灯 限速 安全员——对市区适用对高速就是灾难2.Raft 的串行写入会彻底毁掉 Kafka 的吞吐Raft 的硬约束Leader 必须串行处理请求一次一个不能并行每次写都要等多数派 ACK→同步等待Kafka 的现实需求生产者要批量发送batch.size攒 16KB /linger.ms攒 5ms顺序写磁盘PageCache 顺序 IO→ 千万级/s如果强制 Raft 串行 →吞吐从百万级掉到几千级——Kafka 死了具体数字对比协议单 Partition 吞吐延迟Raft强一致~1-3 万/s10-50msKafka ISRacks1~10-30 万/s1-3msKafka ISRacksall~5-10 万/s5-10msKafka 选了 ISR 而不是 Raft本质是用可控的弱一致换数量级的吞吐提升。3.Kafka 已经通过 Partition 实现了分片 Raft的效果很多人没意识到Kafka 的每个 Partition 内部本身就是一个简化版 Raft。一个 Partition- Leader处理读写- Follower 列表从 Leader 拉数据- ISR同步副本集合- 故障时从 ISR 选新 Leader和 Raft 的对比机制RaftKafka PartitionLeader 选举Term 多数派投票Controller 选主ZK/KRaft 协调日志复制强同步多数派确认异步拉取replica.fetch.min.bytes一致性保证强一致最终一致acksall 时接近强一致水平扩展难多节点难分片天然支持加 Partition 就行Kafka 等于把 Raft 拆成 N 份每份独立并行跑——这是和 Raft 根本不同的设计哲学。4.Kafka 牺牲一致性换水平扩展的极限Raft 的扩展性瓶颈增加节点 多数派增加 写延迟增加5 节点 Raft写要 3 个 ack7 节点 Raft写要 4 个 ack节点越多写越慢——这就是为什么 etcd 推荐 3/5 节点不上 7Kafka 的扩展思路增加节点 增加 Partition 数 总吞吐线性增长100 个 Partition × 10 万/s 1000 万/sRaft 永远做不到这是分布式系统设计里分片Sharding的胜利——Raft 没分片所以扩不动Kafka 天然分片所以能扩到上千节点。5.Kafka 要回溯消费Raft 不支持Kafka 的核心能力消费者可以重新消费历史消息重置 offset 到任意位置这要求消息持久化在磁盘保留一段时间如 7 天Raft 的设计目标数据提交后即可被状态机消费没有保留概念——提交即应用应用即删除想要回放在 Raft 之上再搭一套那不是 Raft 本身的能力这俩的设计目标不同——Kafka 选 ISR 配log.retention.hours保留数据Raft 是提交即消费。6.Kafka 的 Quorum 需求是分区级别不是集群级别RabbitMQ Quorum Queue 的 Quorum一个队列的副本集合3 个节点Quorum2多数派确认Kafka 的类 Quorum一个 Partition 的 ISR 集合可能是 3 个 broker 中的 2 个min.insync.replicas2acksall 时要 2 个 ISR 确认关键差异RabbitMQ 一个集群可能就 3-5 个节点 → Quorum 简单Kafka 一个集群可能几百个 broker几千个 Partition→不可能对整个集群做 RaftRaft 在大规模集群里根本行不通——把 Raft 跑在几百节点上选主一次要等所有节点投票 → 选主风暴 → 集群崩。三、Kafka 实际用了类 Quorum的什么机制三个关键配置记住就够用# 1. 副本数影响 Quorum 大小default.replication.factor3# 2. 最少同步副本数Quorum 硬约束min.insync.replicas2# → acksall 时必须有 2 个 ISR 副本确认才返回# 3. 生产者确认级别acksall# → Leader 等所有 ISR 同步完才返回这三个配置组合 Kafka 的等效 Quorum3 副本2 个 ISR 确认可以容忍 1 个节点挂掉不丢数据和 Raft 的 Quorum2 在数据安全上等价但没有 Raft 的强约束如 Term、日志顺序、串行写入——这给了 Kafka 巨大的性能空间。unclean.leader.election.enablefalse防脑裂的关键Kafka 默认是true为了可用性金融场景必须设falseunclean.leader.election.enablefalse 的含义- 挂掉的 Follower 恢复后只在 ISR 列表里才能成为 Leader- 已经被踢出 ISR 的副本数据落后的不能选主- 这避免了数据落后的副本选为主丢消息⚠️ 代价所有 ISR 都挂时Partition 不可用高可用受损✅ 收益杜绝脑裂和数据丢失强一致这个开关本质上就是在 AP 和 CP 之间切换trueAP 优先牺牲一致性保可用性falseCP 优先牺牲可用性保一致性四、面试话术Kafka 不用 Raft 是架构基因决定的——Raft 是为强一致设计的串行写入、多数派确认、Term 选举但 Kafka 的核心诉求是高吞吐 水平扩展。Raft 一上单 Partition 吞吐从 10 万级掉到 1 万级Kafka 死了。Kafka 用了类 Quorum的 ISR 机制——replication.factor3 min.insync.replicas2 acksall就是它的等效 Quorum能容忍 1 挂、零丢失。再配合unclean.leader.election.enablefalse防止脑裂——这套组合在数据安全上和 Raft 等价但保留了 Kafka 的高吞吐能力。本质区别Raft 是一个队列的强一致Kafka 是一千个分片的高吞吐——设计目标不同没有对错。五、再深一层CAP 三选一的体现系统CAP 选型体现RabbitMQ Quorum QueueCP强一致Raft 协议挂了不可用但数据安全Kafka默认AP可用性ISR 机制挂掉副本会降级服务Kafkaacksall uncleanfalseCP强制强一致牺牲可用性保一致etcdCPRaft挂了不可用CassandraAP最终一致Kafka 默认是 AP但配置到位可以接近 CP——这是它灵活的地方。六、对比表最终版维度KafkaISRRabbitMQQuorumRaft 协议本身设计目标吞吐投递可靠强一致CAP 选型AP可配 CPCPCP副本同步异步拉取同步推送同步推送多数派确认手动配min.insync.replicas内置强制串行写入❌可批量✅✅水平扩展✅ 几千 Partition❌ 队列不能分片❌ 节点越多越慢吞吐/Partition10-30 万/s1-3 万/s1-3 万/s回溯消费✅ 天然❌ 不支持❌ 不支持脑裂保护uncleanfalseQuorum 强约束Term 机制适用场景日志、流计算、削峰订单、支付、RPC配置中心、服务发现七、一句话总结Kafka 不用 Raft 是因为它要的是分片高吞吐不是单点强一致。ISR 机制 弱化的 Quorum 高吞吐配上acksall min.insync.replicas2 uncleanfalse就能在数据安全上等价 Raft但没有 Raft 的强约束串行/Term/严格顺序——这给了 Kafka 数量级的性能优势。