一句话回答
Kafka 不是"一个队列"需要强一致,而是"一个日志流"需要高吞吐 。Raft 是为强一致设计的强约束协议,跟 Kafka 的基因(高吞吐、水平扩展、回溯消费)天然冲突。Kafka 用了"类 Quorum 思想"但更松散的 ISR 机制------这是它在 CAP 里选了 AP 之后的工程妥协。
一、先理解:Raft 的本质是什么?
Raft 是一个强一致 + 严格约束的协议:
Raft 的硬性要求:
-
任何写入必须被多数派(Quorum)确认才返回
-
Leader 必须串行处理所有写请求(不能并行)
-
每次 Leader 变更都要 Term 自增
-
Follower 只能从当前 Leader 同步(拒绝旧 Leader)
-
提交(commit)必须按日志顺序
-
任何不一致都会被检测和修复
这套机制换来的:强一致 + 不会丢数据 + 不会脑裂。
代价 :每次写都要等多数派网络往返------延迟是硬伤。
二、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 万/s | 10-50ms |
| Kafka ISR(acks=1) | ~10-30 万/s | 1-3ms |
| Kafka ISR(acks=all) | ~5-10 万/s | 5-10ms |
Kafka 选了 ISR 而不是 Raft,本质是用"可控的弱一致"换"数量级的吞吐提升"。
3. Kafka 已经通过 Partition 实现了"分片 Raft"的效果
很多人没意识到:Kafka 的每个 Partition 内部,本身就是一个简化版 Raft。
一个 Partition:
-
Leader(处理读写)
-
Follower 列表(从 Leader 拉数据)
-
ISR(同步副本集合)
-
故障时从 ISR 选新 Leader
和 Raft 的对比:
| 机制 | Raft | Kafka Partition |
|---|---|---|
| Leader 选举 | Term + 多数派投票 | Controller 选主(ZK/KRaft 协调) |
| 日志复制 | 强同步,多数派确认 | 异步拉取 (replica.fetch.min.bytes) |
| 一致性保证 | 强一致 | 最终一致(acks=all 时接近强一致) |
| 水平扩展 | 难(多节点难分片) | 天然支持(加 Partition 就行) |
Kafka 等于"把 Raft 拆成 N 份,每份独立并行跑"------这是和 Raft 根本不同的设计哲学。
4. Kafka 牺牲一致性换"水平扩展的极限"
Raft 的扩展性瓶颈:
- 增加节点 = 多数派增加 = 写延迟增加
- 5 节点 Raft:写要 3 个 ack
- 7 节点 Raft:写要 4 个 ack
- 节点越多写越慢------这就是为什么 etcd 推荐 3/5 节点,不上 7
Kafka 的扩展思路:
- 增加节点 = 增加 Partition 数 = 总吞吐线性增长
- 100 个 Partition × 10 万/s = 1000 万/s
- Raft 永远做不到
这是分布式系统设计里"分片(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 个节点)
- Quorum=2(多数派确认)
Kafka 的"类 Quorum":
- 一个 Partition 的 ISR 集合(可能是 3 个 broker 中的 2 个)
min.insync.replicas=2(acks=all 时要 2 个 ISR 确认)
关键差异:
- RabbitMQ 一个集群可能就 3-5 个节点 → Quorum 简单
- Kafka 一个集群可能几百个 broker,几千个 Partition → 不可能对整个集群做 Raft
Raft 在大规模集群里根本行不通------把 Raft 跑在几百节点上?选主一次要等所有节点投票 → 选主风暴 → 集群崩。
三、Kafka 实际用了"类 Quorum"的什么机制?
三个关键配置(记住就够用)
1. 副本数(影响 Quorum 大小)
default.replication.factor=3
2. 最少同步副本数(Quorum 硬约束)
min.insync.replicas=2
→ acks=all 时,必须有 2 个 ISR 副本确认才返回
3. 生产者确认级别
acks=all
→ Leader 等所有 ISR 同步完才返回
这三个配置组合 = Kafka 的"等效 Quorum":
- 3 副本
- 2 个 ISR 确认
- 可以容忍 1 个节点挂掉,不丢数据
和 Raft 的 Quorum=2 在数据安全上等价 ,但没有 Raft 的强约束(如 Term、日志顺序、串行写入)------这给了 Kafka 巨大的性能空间。
unclean.leader.election.enable=false(防脑裂的关键)
Kafka 默认是 true(为了可用性),金融场景必须设 false:
unclean.leader.election.enable=false 的含义:
-
挂掉的 Follower 恢复后,只在 ISR 列表里才能成为 Leader
-
已经被踢出 ISR 的副本(数据落后的)不能选主
-
这避免了"数据落后的副本选为主,丢消息"
⚠️ 代价:所有 ISR 都挂时,Partition 不可用(高可用受损)
✅ 收益:杜绝脑裂和数据丢失(强一致)
这个开关本质上就是"在 AP 和 CP 之间切换":
true:AP 优先(牺牲一致性保可用性)false:CP 优先(牺牲可用性保一致性)
四、面试话术
"Kafka 不用 Raft 是架构基因决定的------
Raft 是为强一致设计的 (串行写入、多数派确认、Term 选举),但 Kafka 的核心诉求是高吞吐 + 水平扩展 。Raft 一上,单 Partition 吞吐从 10 万级掉到 1 万级,Kafka 死了。
Kafka 用了"类 Quorum"的 ISR 机制 ------
replication.factor=3 + min.insync.replicas=2 + acks=all就是它的等效 Quorum,能容忍 1 挂、零丢失。再配合
unclean.leader.election.enable=false防止脑裂 ------这套组合在数据安全上和 Raft 等价,但保留了 Kafka 的高吞吐能力。本质区别 :Raft 是'一个队列的强一致',Kafka 是'一千个分片的高吞吐'------设计目标不同,没有对错。"
五、再深一层:CAP 三选一的体现
| 系统 | CAP 选型 | 体现 |
|---|---|---|
| RabbitMQ Quorum Queue | CP(强一致) | Raft 协议,挂了不可用但数据安全 |
| Kafka(默认) | AP(可用性) | ISR 机制,挂掉副本会"降级服务" |
| Kafka(acks=all + unclean=false) | CP(强制强一致) | 牺牲可用性保一致 |
| etcd | CP | Raft,挂了不可用 |
| Cassandra | AP | 最终一致 |
Kafka 默认是 AP,但配置到位可以接近 CP------这是它灵活的地方。
六、对比表(最终版)
| 维度 | Kafka(ISR) | RabbitMQ(Quorum) | Raft 协议本身 |
|---|---|---|---|
| 设计目标 | 吞吐 | 投递可靠 | 强一致 |
| CAP 选型 | AP(可配 CP) | CP | CP |
| 副本同步 | 异步拉取 | 同步推送 | 同步推送 |
| 多数派确认 | 手动配(min.insync.replicas) | 内置 | 强制 |
| 串行写入 | ❌(可批量) | ✅ | ✅ |
| 水平扩展 | ✅ 几千 Partition | ❌ 队列不能分片 | ❌ 节点越多越慢 |
| 吞吐/Partition | 10-30 万/s | 1-3 万/s | 1-3 万/s |
| 回溯消费 | ✅ 天然 | ❌ 不支持 | ❌ 不支持 |
| 脑裂保护 | unclean=false |
Quorum 强约束 | Term 机制 |
| 适用场景 | 日志、流计算、削峰 | 订单、支付、RPC | 配置中心、服务发现 |
七、一句话总结
Kafka 不用 Raft 是因为它要的是"分片高吞吐",不是"单点强一致" 。ISR 机制 = 弱化的 Quorum + 高吞吐 ,配上
acks=all + min.insync.replicas=2 + unclean=false就能在数据安全上等价 Raft ,但没有 Raft 的强约束(串行/Term/严格顺序)------这给了 Kafka 数量级的性能优势。