面试官:Kafka 为什么比 RocketMQ 快?——不是快,是场景不同

引言

面试场景还原:

面试官:"Kafka 为什么比 RocketMQ 快?"
候选人:"因为 Kafka 有零拷贝、顺序写、批量发送。"
面试官微微一笑:"那 RocketMQ 团队为什么不把这些抄过去?是技术不行吗?"
候选人:?

这一问就露馅了。因为题目的前提就是错的——Kafka 不是"更快的 MQ",RocketMQ 也不是"慢一点的 MQ"。它们是两个物种:

  • Kafka 从 LinkedIn 的日志管道演化而来,本质是分布式日志系统,为海量数据的高吞吐流转而生
  • RocketMQ 从阿里双 11 交易链路演化而来,本质是业务消息中间件,为交易场景的功能完备性而生

先亮核心结论:高吞吐的数据链路选 Kafka,业务交易链路选 RocketMQ。我们公司生产上两者并存——埋点日志、CDC、Flink 流处理走 Kafka(日处理 40 亿条);订单、支付、库存等业务消息走 RocketMQ,各干各的,谁也没想替代谁。

这篇文章按《本地缓存三巨头》的套路,从 5 个维度拆解两者差异:简介 → 架构对比 → 吞吐实测 → 功能完备性 → 延迟与消费体验,最后给出"谁也替代不了谁的 3 + 3 张底牌"和选型决策树。看完你不但能答好这道面试题,还能答好那道追问。


一、两位选手简介

1.1 Kafka

出身:2011 年 LinkedIn 开源,最初解决的是"网站行为日志从应用到 Hadoop 的搬运",后来 Apache 顶级项目,2021 年进入 Confluent 主导的时代。

定位:分布式提交日志(Commit Log)/ 流数据平台——把它当"MQ"其实是降维使用。

特点:

  • 存储模型:每个分区一个独立日志文件,消费者拉取的是连续文件区间(这个设计后面反复出现)
  • 四板斧:顺序写 + PageCache + 零拷贝 sendfile + 批量压缩(详见《Kafka 为什么这么快》)
  • 分区并行:吞吐随分区数近乎线性扩展
  • 生态:Flink/Spark/Debezium/Kafka Connect 的默认数据总线

现状:4.x 时代 KRaft 全面取代 ZooKeeper,运维复杂度显著下降;大数据领域事实标准。

# Kafka 的"快"大半写在客户端配置里——复杂性给了客户端
batch.size=65536           # 攒批 64KB
linger.ms=5                # 最多等 5ms
compression.type=zstd      # 攒完压缩
enable.idempotence=true    # 幂等,重试不乱序

1.2 RocketMQ

出身:2012 年阿里开源(前身 MetaQ),为双 11 交易链路打造,同年起承载双十一全部交易消息。

定位:业务消息中间件——消息队列里的"功能完全国产化重型选手"。

特点:

  • 存储模型:所有队列的消息混存在单个 CommitLog,ConsumeQueue 只做索引(这个设计也后面反复出现)
  • 四张功能牌:事务消息、延迟/定时消息、消息轨迹、重试与死信——全是服务端内建
  • 架构轻组件:NameServer 无状态,不用 ZooKeeper

现状:Apache 顶级项目,5.x 引入 Proxy 模式(gRPC 多语言)与存储 KV 分离优化,国内金融/电商渗透率极高。

// RocketMQ 的"全"写在 API 里——复杂性收进了服务端
message.setDelayTimeSec(1800);              // 延迟消息:30 分钟后投递,一行

producer.sendMessageInTransaction(msg, order);   // 事务消息:半消息 + 本地事务 + 回查

// 消费失败 → 自动进重试队列(16 级退避)→ 耗尽自动进死信 %DLQ%
// 发送 → 存储 → 消费全程有轨迹可查
// —— 以上这些,在 Kafka 里全部要自己搭

1.3 一句话画像

KafkaRocketMQ
像什么一本可订阅的分布式账本(追加写、随便翻旧账)一台功能齐全的挂号机(叫号、改约、投诉记录一应俱全)
优化目标每秒流转更多字节每条业务消息更可靠、更可控

二、架构对比:两张图看懂"快"与"全"的分歧

2.1 两张架构图

【Kafka:轻 Broker,重客户端】

┌──────────────┐      ┌────────────────────────────────────┐      ┌────────────────┐
│   producer    │      │           Broker 集群                │      │ consumer group  │
│  攒批/压缩    │─────▶│ P0: [log文件 + index] leader         │─────▶│ 拉模式 poll      │
│  分区路由     │      │      follower(ISR 副本)             │      │ 位点自己管理     │
│  重试/幂等    │      │ P1: [log文件 + index] ...            │      │ 故障转移自己做   │
└──────────────┘      └────────────────────────────────────┘      └────────────────┘
                              ▲
                    元数据:KRaft(4.x 起,无 ZooKeeper)

  客户端承担:攒批、压缩、分区路由、重试幂等、位点管理
  服务端只做一件事:把日志文件顺序写好、按区间读出去
【RocketMQ:重 Broker,轻客户端】

┌─────────┐ 路由   ┌─────────────┐      ┌─────────────────────────────────┐
│producer │◀─────▶│  NameServer  │      │ Broker Master                    │
│         │───────│ (无状态 ×2) │─────▶│  CommitLog:所有队列混存,顺序追加  │
└─────────┘       └─────────────┘      │  ConsumeQueue:按队列的索引        │
                                       │  IndexFile:按 key 查消息          │
┌─────────┐  push(长轮询)            │  Slave 同步复制                   │
│consumer │◀──────────────────────────│  事务回查 / 延迟队列 / 重试队列     │
└─────────┘                           └─────────────────────────────────┘
                                        ▲
                              IndexFile + 轨迹 Topic:消息轨迹的数据基础

  服务端承担:过滤、轨迹、事务回查、延迟调度、重试死信
  客户端只做一件事:发消息、注册监听器

2.2 关键差异表

设计点KafkaRocketMQ带来的后果
元数据管理KRaft(早期 ZK)NameServer 无状态 ×2RocketMQ 组件更轻,但路由感知有秒级延迟
存储模型每分区独立日志文件单 CommitLog 混存 + ConsumeQueue 索引决定了两者 IO 路径的分野(见 2.3)
数据传输sendfile 零拷贝mmap(MappedByteBuffer)Kafka 消费路径 CPU 更省
批量发送客户端默认攒批显式 batch API(≤4MB),默认逐条Kafka 高吞吐开箱即用
功能驻留客户端 + 生态组件服务端内建Kafka 功能升级要动客户端;RocketMQ 只动服务端
消费方式pull,参数自己调push(长轮询),开箱低延迟见第五章

2.3 追问的正解:为什么 RocketMQ 不抄零拷贝?

面试官那句"为什么不抄过去",答案就藏在存储模型里。

sendfile 生效的前提是:文件连续区间 → 网卡,中间不碰数据内容。Kafka 的消费者按 offset 拉取,offset 直接映射为"分区日志文件里的连续字节区间",数据全程不用进用户态,所以 sendfile 一路绿灯。

再看 RocketMQ 的消费路径:

消费者要拉 queue-3 的 offset=1000 的消息:

ConsumeQueue 定位 → 得到 CommitLog 的物理偏移
        ↓
CommitLog 是【所有 Topic 所有队列混存】的同一个文件
——这一段里可能混着订单、库存、短信的消息
        ↓
必须解析消息体:按 Tag/SQL92 过滤、处理属性、埋轨迹、查事务状态
        ↓
数据必须进用户态 → sendfile 失效

也就是说:不是 RocketMQ 不想要零拷贝,是"服务端要看得见消息内容"这个功能定位,逼着数据走进用户态——事务回查、延迟调度、Tag 过滤、轨迹记录,每一个服务端功能都建立在"broker 能解析每条消息"之上。作为折中,RocketMQ 用 mmap 把 CommitLog 映射进内存(省掉内核态到用户态的一次拷贝),配合 transientStorePool 优化写路径。

这就是全文最重要的一句话:Kafka 的快和 RocketMQ 的全,是同一个架构分歧的两面——Kafka 把消息当"不透明字节流"存,才换来零拷贝和极致吞吐;RocketMQ 把消息当"一等公民对象"存,才换来服务端功能全家桶。 存储模型决定了 IO 路径,IO 路径决定了性能上限。


三、维度一:吞吐量——Kafka 的主场

3.1 测试环境

项目配置
机器3 台 16C 64G,NVMe SSD,万兆内网(同一批机器分别部署)
Kafka 3.83 Broker,RF=3,acks=all + 幂等,zstd(与《Kafka 为什么这么快》同一套基准)
RocketMQ 5.x3 Master + 3 Slave,同步复制;2KB 订单消息,与 Kafka 相同
压测工具各自官方 benchmark 脚本调到同规格

3.2 测试结果

持久化语义KafkaRocketMQ
副本可靠 + 异步落盘80 万 msg/s(acks=all,靠副本代替刷盘)32 万 msg/s(异步刷盘 + 同步复制)
强持久化无此概念(Kafka 不支持 broker 级同步刷盘)15 万 msg/s(同步刷盘 + 同步复制)
磁盘 util< 30%(热数据在 PageCache)60%+(每条消息都要构建索引 + 刷盘)

先解释一个公平性问题:Kafka 用多副本的网络冗余代替 fsync 保数据安全(《Kafka 为什么这么快》8.2 讲过原理),RocketMQ 的同步刷盘是真·每次落盘,语义更重。所以对比"同级语义"看第二行:Kafka 80 万 vs RocketMQ 32 万,差距约 2.5 倍;对比"最强持久化"差距约 5 倍。

3.3 数据分析

差距拆到三个来源,每个都能在架构图里找到对应:

吞吐差距来源Kafka 的做法RocketMQ 的做法损失量级
消费路径sendfile,数据不进用户态必须解析消息体,mmap 进用户态消费端 CPU 开销高数倍
攒批客户端默认攒批 + 压缩默认逐条同步发,batch 要显式开单条 RPC 成本 ×N
服务端功能开销消息是不透明字节流,broker 只管搬运每条消息建 ConsumeQueue 索引、可埋轨迹、可过滤每条多几次内存操作与 IO

注意分区并行这一项不构成差距:RocketMQ 的队列数同样可以水平扩展,混存 CommitLog 的顺序追加也足够快——瓶颈不在"分区能力",而在"每条消息的服务端处理成本"和"无法 sendfile 的消费路径"。

3.4 结论

吞吐量:Kafka > RocketMQ,同语义差距 2.5 倍,最强持久化场景差距 5 倍。

但请务必看清另一半事实:RocketMQ 的 15~32 万 msg/s,对绝大多数业务 Topic 都是天文数字——日均 1 亿条订单消息折算峰值也就 2000/s,离 15 万差两个数量级。吞吐差距只在日志/埋点/CDC 这种"每日 TB 级"的数据链路上才是决定性的。所以"谁快"根本不是业务选型的正确问法——这正是面试官想听的下半句。


四、维度二:功能完备性——RocketMQ 的主场

吞吐输了 2.5 倍,RocketMQ 在这一章连本带利赢回来。四张功能牌,每一张 Kafka 都有"平替",但每一张都要自己搭。

4.1 事务消息:半消息 + 本地事务 + 回查

业务最经典的诉求:"本地事务成功 + 消息一定发出去"要原子。RocketMQ 的解法:

producer.setTransactionListener(new TransactionListener() {
    // 第一步:发半消息(消费者不可见)→ 再执行本地事务
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        orderDao.createAndDeductStock((Order) arg);     // 本地事务
        return LocalTransactionState.COMMIT_MESSAGE;    // 成功 → 消息可见
    }

    // 第二步:若迟迟没收到确认,Broker 主动回查
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        return orderDao.exists(msg.getKeys())
                ? LocalTransactionState.COMMIT_MESSAGE     // 事务其实成功了 → 补提交
                : LocalTransactionState.ROLLBACK_MESSAGE;  // 确实失败 → 丢弃半消息
    }
});

Kafka 的事务是什么?是流处理 exactly-once(read-process-write 幂等),解决的是"消费-加工-再生产"不出重复,做不了"DB 落库 + 发消息"的业务原子性。

RocketMQ 事务消息Kafka 事务
解决的问题本地事务与发消息的原子性流处理端到端 exactly-once
机制半消息 + 确认 + 回查PID + epoch + two-phase commit
典型场景下单扣库存 + 发事件Flink 状态后端 checkpoint
Kafka 的平替本地消息表 / Outbox 模式 + 扫描补偿——(它本来就是干这个的)

Kafka 不是做不了业务原子性——Outbox 模式是成熟方案——但每个业务都要建一张消息表、写一个扫描补偿任务、处理补偿的幂等,这是持续性的开发与心智成本。

4.2 延迟/定时消息:原生一行

订单 30 分钟未支付自动关闭,是电商铁打的刚需。RocketMQ:

// 4.x:18 个固定等级(1s/5s/10s/30s/1m...2h)
message.setDelayTimeLevel(16);        // 30 分钟

// 5.x:任意时刻,秒级精度
message.setDelayTimeSec(1800);        // 1800 秒后投递

Kafka 没有原生延迟消息,平替方案都不优雅:

平替方案代价
DB 扫表(状态 + 超时时间)扫描频率与时效性矛盾,大表扫描压力大
多级延迟 Topic(delay-1m/5m/30m)+ 转发Topic 数量膨胀,要自己维护转发服务
独立调度系统(xxl-job 等)多一个中间件,消息与调度状态两处管

4.3 消息轨迹:全链路可查

开启 msgTrace 后,一条消息从**生产(谁发的、耗时多少)→ 存储(落在哪、什么状态)→ 消费(谁消费的、重试几次、何时 ACK)**全程留痕,控制台一查到底。线上排查"消息到底发出去没/消费了没",从翻各服务日志拼接链路,变成一次查询。

Kafka 平替:客户端埋点 + 统一 trace 串联(可用《Kafka 运维必备工具清单》里的工具辅助)。能做,但埋点代码分散在生产端、消费端、业务代码三处,且没有现成的可视化界面——轨迹是典型的"自建要花一百人力,内建只要一个开关"的功能。

4.4 重试与死信:内建全家桶

RocketMQ 消费失败的处理是全自动的:

消费返回 RECONSUME_LATER
   ↓ 自动投递到系统重试 Topic:%RETRY%{consumerGroup}
   ↓ 16 级延迟递增退避:10s → 30s → 1m → ... → 2h
   ↓ 重试 16 次仍失败
   → 自动进入死信 Topic:%DLQ%{consumerGroup},控制台可视化管理

Kafka 没有内建重试队列(顺序消费场景的坑在《Kafka 顺序消息实战》8.5 详细拆过)。Spring Kafka 的 @RetryableTopic 是最接近的平替:

@RetryableTopic(attempts = "5", backoff = @Backoff(delay = 1000, multiplier = 2))
@KafkaListener(topics = "order-events")
public void handle(OrderEvent event) { ... }
// 背后自建 retry-0..4 + dlt 一串内部 Topic,运维和监控都自己兜

4.5 结论

功能完备性:RocketMQ 完胜。四张牌在 Kafka 里都有技术平替,但每一张都是"自建组件 + 持续运维",而 RocketMQ 是"配置项 + 控制台"。业务消息选 RocketMQ,本质是花钱买现成——吞吐上让出的 2.5 倍,换来的是团队不用造这四个轮子。


五、维度三:延迟与消费体验

5.1 push 与 pull 的体验差异

Kafka(pull)RocketMQ(push 长轮询)
消费模型客户端循环 poll,fetch.min.bytes / fetch.max.wait.ms 自己调客户端注册 Listener,服务端推
低流量延迟调不好会带出"poll 间隔级"延迟开箱即低延迟
高吞吐延迟批量生效后更稳P99 抬升更早
心智负担客户端参数多(攒批/拉取/位点)参数少,服务端代办

5.2 实测

场景Kafka P99RocketMQ P99
低流量(1 万 msg/s)12 ms(poll 调优后)6 ms
高流量(50 万 msg/s)15 ms(批量兜住了)28 ms

两个场景各有胜者:低流量下 RocketMQ 的 push 更快(没有 poll 周期的取整损耗),高吞吐下 Kafka 的攒批更稳(延迟换吞吐的收益兑现)。对绝大多数业务——P99 都在 30ms 以内——延迟不构成选型分水岭。

5.3 结论

延迟与消费体验:日常业务两者都合格;RocketMQ 上手更省心,Kafka 高吞吐下更稳,Kafka 的延迟调优(攒批与拉取参数)依赖对《Kafka 为什么这么快》那套机制的理解。


六、不能互相替代的能力:各 3 张底牌

这是面试的决胜题,也是选型的最终依据:各自有对方很难甚至无法补齐的能力。

6.1 Kafka 独有的 3 张底牌

① 大数据生态的总线地位

Flink、Spark Streaming、Debezium(CDC)、Kafka Connect、Schema Registry、ksqlDB……整个数据基础设施默认 Kafka 是入口。RocketMQ 有 connector 生态,但接入一个新组件通常要桥接层,而 Kafka 是"原生第一公民"。这不是性能差距,是十年生态积累的位差,短期内无解。

② 日志模型 + 任意位点回放

Kafka 本质是"可订阅的分布式日志":数据保留期可以设到月级,消费者位点自己管理,随时 --reset-offsets --to-datetime 回放到任意时刻——数仓重算、新服务冷启动订阅三个月历史数据、故障后重新消费,都是日常操作。RocketMQ 也能重置位点,但它定位是"队列",默认保留 48~72 小时,长期存储成本高。"旧账随时能翻"是数据管道的刚需,是业务队列的奢侈品。

③ 高吞吐下的单位成本

按 3.2 实测折算:同样扛 100 万 msg/s,Kafka 约 3~4 台机器,RocketMQ 同规格要 8~10 台。日志/埋点这种"条数巨大、单条价值低"的流量,成本差就是选型本身。

6.2 RocketMQ 独有的 3 张底牌

① 事务消息

"本地事务 + 发消息"的原子性,Kafka 没有对等物(它的事务是流处理语义)。Outbox 平替可行,但每个业务都要自建补偿与幂等——交易链路里这是核心能力,不是可选项。

② 延迟/定时消息

原生、精确、一行代码。Kafka 的所有平替(扫表/多级 Topic/外挂调度)都比它重一个数量级。关单、超时重试、预约提醒……凡是"到点才发生"的业务,这一张牌就够把天平压过去。

③ 轨迹 + 重试 + 死信的服务端全家桶

排查有轨迹、失败有退避、耗尽有死信、控制台可视化管理。Kafka 要拼出同等能力,需要 trace 埋点 + @RetryableTopic + 死信监控 + 自建管控台四件套,且长期维护成本摊在每个人身上。

6.3 底牌对照表

能力KafkaRocketMQ补齐成本
大数据生态位原生需桥接每个组件一次桥接,持续维护
位点回放/长期存储月级保留 + 时间旅行短保留,重放受限换存储或外挂归档
高吞吐单位成本3~4 台 / 百万 msg/s8~10 台 / 百万 msg/s直接的机器账
事务消息Outbox 自建原生每业务一张表 + 补偿任务
延迟消息自建三选一原生一行一个独立调度组件
轨迹/重试/死信四件套自建原生全家桶埋点 + 管控台 + 长期运维

七、总结

星级对比表

维度KafkaRocketMQ
极限吞吐★★★★★★★★
高吞吐单位成本★★★★★★★★
开箱延迟★★★★★★★★★
事务消息★★★★★★★
延迟/定时消息★★★★★★
消息轨迹★★★★★★★
重试与死信★★★★★★★
大数据/流生态★★★★★★★
位点回放/长期存储★★★★★★★★
顺序消息★★★★★★★★
运维友好度★★★★★★★★

(顺序消息两者打平:Kafka 分区键 hash,RocketMQ MessageQueueSelector 显式路由,细节见《Kafka 顺序消息实战》;KRaft 之后 Kafka 运维复杂度大幅缓解,两者打平。)

各自适用场景

场景选谁一句话理由
日志/埋点/用户行为采集Kafka吞吐 + 成本 + 生态
CDC 数据管道(MySQL→数仓)KafkaDebezium 原生对接
Flink/Spark 流处理Kafka默认数据总线
需要回放重算的链路Kafka位点自由
订单/支付/库存交易消息RocketMQ事务 + 轨迹 + 重试
关单/超时提醒/定时触发RocketMQ原生延迟消息
金融级强持久化业务RocketMQ同步刷盘可选
中小团队业务解耦RocketMQ开箱即用,心智负担小

选型决策树

新系统要上消息队列,怎么选?
├── 流量是"数据"(日志/埋点/CDC/流处理/数仓管道)
│   └── Kafka,不用纠结
├── 流量是"业务"(交易/订单/状态变更/异步任务)
│   ├── 需要事务原子性 / 延迟投递 / 轨迹审计 → RocketMQ
│   └── 只要可靠 pub-sub,且团队已有成熟 Kafka 体系
│       └── Kafka 也能干,接受 Outbox/重试/轨迹的自建成本
├── 两者都要?→ 主流大厂方案:按消息角色分,不按"谁快"分
│   └── 数据链路 → Kafka;业务链路 → RocketMQ
└── 团队只有一个中间件 headcount
    └── 少即是多:一个 MQ 吃透,好过两个都半吊子

面试标准答案(背这四句)

  1. 纠偏:"这个问题的前提需要修正——Kafka 和 RocketMQ 定位不同:前者是分布式日志系统/数据管道,后者是业务消息中间件,不是同一赛道的快慢之分。"
  2. 快的原因:"Kafka 快在零拷贝、客户端攒批、分区并行,根源是'每分区独立日志'的存储模型让数据可以不进用户态,走 sendfile 直达网卡。"
  3. 为什么 RocketMQ 不抄:"因为它的功能定位要求服务端解析每条消息——事务回查、延迟调度、过滤、轨迹都建立在数据进用户态之上,sendfile 就失效了。这是功能定位的结构性代价,不是技术能力差距。"
  4. 结论:"高吞吐数据链路选 Kafka,业务交易链路选 RocketMQ,两者在实践中是并存互补,不是二选一。"

一句话

Kafka 和 RocketMQ 的性能差距,是同一个架构分歧的两面:Kafka 把消息当"不透明的字节流"存——每分区一个日志文件、broker 只管顺序写和按区间读,才换来 sendfile 零拷贝、客户端攒批和 2.5~5 倍的吞吐,代价是功能全推给客户端和生态组件;RocketMQ 把消息当"一等公民对象"存——单 CommitLog 混存、broker 解析每条消息,才换来事务回查、延迟调度、轨迹与重试的全家桶,代价是数据必须进用户态、吞吐让出 2.5 倍——所以面试答"零拷贝+批量+分区并行"只值一半分,另一半在"为什么 RocketMQ 不抄过去":不是不能,是它的存储模型从第一天就选择了另一条路;选型亦然——高吞吐的数据链路交给 Kafka,功能敏感的业务链路交给 RocketMQ,让账本归账本、挂号机归挂号机。

关键数据

  • 同环境同语义实测:Kafka 80 万 msg/s vs RocketMQ 32 万(异步刷盘),差距约 2.5 倍;同步刷盘 15 万,差距约 5 倍
  • 但业务日均 1 亿条消息峰值仅约 2000/s,离 RocketMQ 上限差两个数量级——吞吐差距在业务场景不构成痛点
  • 低流量延迟:RocketMQ push P99 6ms vs Kafka 12ms;高流量:Kafka 15ms vs RocketMQ 28ms——各有胜场
  • 事务消息平替成本:每业务一张本地消息表 + 扫描补偿任务 + 幂等处理
  • 100 万 msg/s 的机器账:Kafka 约 3~4 台 vs RocketMQ 约 8~10 台——大数据链路的成本差就是选型

给团队的建议

项目状态建议
只有一个 MQ 的团队按消息角色选型后再统一,别让一个 MQ 既扛日志又扛交易
双 MQ 并存的团队明确边界:数据 Topic 归 Kafka、业务 Topic 归 RocketMQ,写进架构规范
已全司 Kafka业务消息硬用可以,Outbox/重试/轨迹的自建成本立项算清,别温水煮青蛙
已全司 RocketMQ日志/埋点/CDC 回流 Kafka,别让交易中间件硬扛 TB 级数据流
面试准备把"为什么不抄零拷贝"这个追问练熟——它同时考架构、存储模型和权衡思维
技术选型评审用本文 6.3 底牌对照表逐项过,而不是凭"听说谁快"

八、常见问题

8.1 RocketMQ 5.x 不是做了很多性能优化吗?还慢吗?

5.x 的存储 KV 分离和 Proxy 模式确实把写入和消费性能拉高了一截,我们同环境复测异步刷盘从 25 万提到 32 万。但定位没变:它依然是"服务端解析消息"的架构,事务/延迟/轨迹/过滤这些功能还是要碰消息内容,sendfile 的路依然走不通——优化缩小的是工程差距,抹不掉的是架构分歧。这反而是答题的好素材:说明你关注版本演进,而不是背三年前的结论。

8.2 我们全司只有 Kafka,业务消息硬用它行不行?

行,但要把四个自建组件的账算清:① 事务原子性 → 每业务一张本地消息表 + 扫描补偿;② 延迟消息 → 扫表或多级延迟 Topic;③ 轨迹 → 客户端埋点 + trace 串联;④ 重试死信 → @RetryableTopic + 死信监控。四个轮子都能造,问题是每个业务都要重复造一遍,且各团队的造法不一。如果业务复杂度到了交易级,引入 RocketMQ 的一次性成本通常低于持续自建的累计成本——按"自建人力 × 业务数"算这笔账。

8.3 为什么 RocketMQ 不把存储改成 Kafka 那样的分区日志,直接抄零拷贝?

改了,服务端功能就没了。轨迹、延迟队列、事务回查、Tag/SQL92 过滤,全都依赖"broker 能拿到并解析全量消息"——单 CommitLog 混存正是为此服务的。5.x 的 KV 分离算是一次折中尝试(索引与消息体分离存储),但"数据不进用户态"和"服务端看得见数据"在原理上互斥。这道题的精髓是:架构没有免费午餐,每一分性能都对应一种功能上的放弃。

8.4 Kafka 事务和 RocketMQ 事务到底差在哪?

完全不同的两个东西。Kafka 事务 = 流处理 exactly-once:用 PID + epoch 保证"消费-加工-生产"链路不重不丢,服务的是 Flink 这类流式计算。RocketMQ 事务 = 业务分布式事务的一半:半消息 + 本地事务 + 回查补偿,服务的是"DB 操作和发消息要么都成要么都不成"。一句话区分:Kafka 保证数据流不出错,RocketMQ 保证业务动作原子。

8.5 那 Pulsar 呢?存算分离不是两边都想要吗?

Pulsar 的确用分层架构(Broker 无状态 + BookKeeper 存储层)兼顾了功能与弹性,云原生场景有优势。但它多一层组件(ZooKeeper + BookKeeper + Broker),运维复杂度最高,国内生产渗透率远低于两者。我的建议:主战场在 Kafka/RocketMQ 之间选,Pulsar 留给云原生 k8s 深度玩家——别为了"看起来先进"引入第三个 MQ。

8.6 顺序消息两者谁做得更好?

打平,但实现路线有趣地不同:Kafka 靠分区键 hash(同实体同分区 + 分区串行消费,《Kafka 顺序消息实战》有完整三板斧),RocketMQ 靠 MessageQueueSelector——路由选择权交给业务代码。这带来一个隐藏差异:Kafka 的 murmur2 硬 hash 遇到扩分区会漂移(《Kafka 顺序消息实战》2.4),RocketMQ 可以在业务层维护 shardingKey→queue 的稳定映射来规避,灵活性略胜。当然前提是你愿意维护这张映射表。


互动话题:你们公司是 Kafka、RocketMQ 还是双 MQ 并存?有没有"用 Kafka 硬扛业务消息然后不停补轮子"或"用 RocketMQ 硬扛数据流被吞吐教做人"的经历?评论区聊聊你们的选型故事,点赞最高的场景我拆成下一篇的完整实战案例。

参考资料


标题:面试官:Kafka 为什么比 RocketMQ 快?——不是快,是场景不同
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/10/24/1792337139939.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消