【MQ】Kafka与RocketMQ深度对比

Kafka与RocketMQ深度对比

前言

很多人问:Kafka 和 RocketMQ 到底该选哪个?这篇文章我们就来深入对比一下这两个消息队列的架构差异、功能特性和性能表现,帮你做出更好的技术选型。

🏠个人主页:你的主页


文章目录


一、一句话总结两者差异

RocketMQ 是在架构上做减法,在功能上做加法

什么意思呢?

  • 架构做减法:简化了 Kafka 的一些设计,让系统更轻量
  • 功能做加法:增加了很多 Kafka 没有的实用功能

但代价是什么?性能。Kafka 每秒能处理 17 万级别的数据,RocketMQ 是 10 万级别。不是 RocketMQ 弱,而是 Kafka 性能太强了。


二、架构上做减法

2.1 用 NameServer 替换 Zookeeper

Kafka 早期依赖 Zookeeper 来管理集群元数据。但 Zookeeper 是一个重量级的分布式协调服务,它能干的事情太多了:

  • 服务注册与发现
  • 分布式锁
  • 配置管理
  • Leader 选举
  • ...

而 Kafka 只用到了其中一小部分功能,这就好比你只想切个苹果,却搬来一整套厨房设备。

RocketMQ 的做法是:既然用不到那么多功能,那就换一个更轻量的组件------NameServer

打个比方

  • Zookeeper 像一个功能齐全的智能家居中控系统,能控制灯光、空调、窗帘、安防...
  • NameServer 像一个简单的通讯录,只负责告诉你"某个服务在哪台机器上"

NameServer 的特点:

特性 说明
无状态 每个 NameServer 节点相互独立,不需要同步数据
轻量 代码量少,部署简单
高可用 挂掉一个不影响其他节点

不过值得一提的是,Kafka 从 2.8.0 版本开始也在逐步移除 Zookeeper 依赖,引入了 KRaft 模式。看来大家都意识到 Zookeeper 太重了。

2.2 简化分区模型

Kafka 把 Topic 拆分成多个 Partition (分区)来提升并发能力,RocketMQ 也一样,只不过换了个名字叫 Queue(队列)。

但两者的存储方式完全不同:

Kafka 的方式

复制代码
Topic
├── Partition-0  →  存储完整消息
├── Partition-1  →  存储完整消息
└── Partition-2  →  存储完整消息

每个 Partition 是一个独立的日志文件,消息直接写在里面。

RocketMQ 的方式

复制代码
Broker
├── CommitLog     →  存储所有消息的完整数据(顺序写入)
├── Queue-0       →  只存储 offset(指向 CommitLog 的位置)
├── Queue-1       →  只存储 offset
└── Queue-2       →  只存储 offset

Queue 里只存简要信息(主要是 offset),真正的消息数据都在 CommitLog 里。

打个比方

  • Kafka 像是每个部门都有自己的档案柜,文件直接放在各自的柜子里
  • RocketMQ 像是公司有一个统一的档案室(CommitLog),各部门只保留一个索引本(Queue),记录"我的文件在档案室的第几页"

三、存储模型的差异

3.1 读取性能对比

因为存储方式不同,读取消息的过程也不一样:

Kafka 读取消息

复制代码
1. 找到对应的 Partition
2. 直接读取消息 ✅ 一次搞定

RocketMQ 读取消息

复制代码
1. 从 Queue 读取 offset
2. 根据 offset 去 CommitLog 读取完整消息

RocketMQ 需要两次读取,看起来效率更低?别急,继续往下看。

3.2 写入性能对比

这里就是 RocketMQ 设计的精妙之处了。

Kafka 的写入问题

假设一个 Broker 上有 100 个 Topic,每个 Topic 有 10 个 Partition,那就是 1000 个 Partition。每个 Partition 对应磁盘上不同位置的文件。

复制代码
写消息到 Topic-A-Partition-0  →  磁盘位置 A
写消息到 Topic-B-Partition-3  →  磁盘位置 B
写消息到 Topic-C-Partition-7  →  磁盘位置 C

虽然单个 Partition 内是顺序写,但多个 Partition 分布在磁盘不同位置,整体上就变成了随机写

打个比方:你要往 10 本不同的笔记本上写字,每写一行就要换一本,手忙脚乱。

RocketMQ 的解决方案

所有消息不管属于哪个 Topic,统统写到同一个 CommitLog 文件里。

复制代码
消息1(Topic-A)→ CommitLog
消息2(Topic-B)→ CommitLog
消息3(Topic-C)→ CommitLog

这样就把随机写变成了顺序写,磁盘 IO 效率大大提升。

打个比方:不管什么内容,都往同一本笔记本上写,写完一页翻下一页,行云流水。

结论:在多 Topic 场景下,RocketMQ 的写入性能优于 Kafka。


四、主从同步机制对比

数据可靠性需要主从备份,两者的同步方式也不同。

Kafka 的主从同步

复制代码
Broker-1 (Leader)
├── Partition-0  ──同步──→  Broker-2 (Follower) Partition-0
├── Partition-1  ──同步──→  Broker-3 (Follower) Partition-1
└── Partition-2  ──同步──→  Broker-2 (Follower) Partition-2

以 Partition 为单位同步,每个 Partition 单独建立同步通道。

RocketMQ 的主从同步

如果还像 Kafka 一样按分区同步,就得把 CommitLog 里的内容拆开,又退化成随机读写了。

所以 RocketMQ 索性直接同步整个 CommitLog 文件

复制代码
Master Broker
└── CommitLog  ──整体同步──→  Slave Broker CommitLog

以 Broker 为单位区分主从,大大简化了备份模型。


五、功能上做加法

RocketMQ 在功能上比 Kafka 丰富很多,这也是很多业务系统选择它的原因。

5.1 消息过滤(Tag)

假设你有一个 user-events 主题,里面有各种用户事件:注册、登录、下单、评论...

Kafka 的做法

java 复制代码
// 消费者拿到所有消息,自己过滤
for (ConsumerRecord record : records) {
    UserEvent event = parse(record);
    if (event.getType().equals("ORDER")) {
        // 处理订单事件
    }
    // 其他事件直接丢弃
}

问题:消费者要拉取所有消息,然后手动过滤,浪费带宽和 CPU。

RocketMQ 的做法

java 复制代码
// 生产者发送时打上 Tag
Message msg = new Message("user-events", "ORDER", data);

// 消费者订阅时指定 Tag,只拉取需要的消息
consumer.subscribe("user-events", "ORDER");

Broker 端直接过滤,消费者只收到自己关心的消息。

5.2 事务消息

两者都支持事务,但含义不同。

Kafka 的事务

保证生产者发送的多条消息,要么全部成功,要么全部失败。

java 复制代码
producer.beginTransaction();
producer.send(msg1);
producer.send(msg2);
producer.send(msg3);
producer.commitTransaction(); // 三条消息原子性提交

这只是发送端的事务

RocketMQ 的事务

保证发消息本地业务操作要么同时成功,要么同时失败。

复制代码
1. 发送半消息(Half Message)到 Broker,消费者暂时看不到
2. 执行本地事务(比如扣款)
3. 根据本地事务结果,提交或回滚消息

举个例子

用户下单扣款场景:

  • 发送"扣款成功"消息到 MQ
  • 执行数据库扣款操作

如果消息发了,但数据库扣款失败了怎么办?RocketMQ 的事务消息可以保证两者一致性。

5.3 延迟队列

场景:用户下单后 30 分钟未支付,自动取消订单。

RocketMQ:原生支持延迟消息

java 复制代码
Message msg = new Message("order-timeout", data);
msg.setDelayTimeLevel(5); // 延迟级别,比如 5 代表 1 分钟
producer.send(msg);

Kafka:不支持,需要自己实现(比如用定时任务扫描)。

5.4 死信队列

消费失败的消息去哪了?

RocketMQ:自动进入死信队列(Dead Letter Queue),方便后续排查和重试。

Kafka:原生不支持,需要自己实现(比如消费失败后手动发到另一个 Topic)。

5.5 消息回溯

想重新消费历史消息怎么办?

功能 Kafka RocketMQ
按 Offset 回溯 ✅ 支持 ✅ 支持
按时间回溯 ✅ 支持(0.10.1+) ✅ 支持

这个功能两者都有,Kafka 从 0.10.1 版本后也支持按时间回溯了。

5.6 功能对比总结

功能 Kafka RocketMQ
消息过滤(Tag) ❌ 不支持 ✅ 支持
事务消息 ⚠️ 仅发送端事务 ✅ 分布式事务
延迟队列 ❌ 不支持 ✅ 支持
死信队列 ❌ 不支持 ✅ 支持
消息回溯 ✅ 支持 ✅ 支持

六、性能差异的根本原因

前面说了,Kafka 性能(17万/秒)比 RocketMQ(10万/秒)高不少。RocketMQ 参考了 Kafka 的设计,为什么性能反而不如?

答案是:零拷贝技术的选择不同

6.1 什么是零拷贝

消息队列的数据存在磁盘上。消费者要读取消息,数据需要从磁盘发送到网络。

传统方式(没有零拷贝):

复制代码
磁盘 → 内核缓冲区 → 用户空间 → Socket缓冲区 → 网卡
       (拷贝1)      (拷贝2)      (拷贝3)      (拷贝4)

4 次拷贝,2 次系统调用,效率很低。

6.2 两种零拷贝方案

方案一:mmap(内存映射)

复制代码
磁盘 → 内核缓冲区 ←映射→ 用户空间 → Socket缓冲区 → 网卡
       (拷贝1)    (不拷贝)         (拷贝2)      (拷贝3)

省掉了内核到用户空间的拷贝,还剩 3 次。

关键点:用户空间可以直接访问数据内容

方案二:sendfile

复制代码
磁盘 → 内核缓冲区 → 网卡
       (DMA拷贝)   (DMA拷贝)

只有 2 次拷贝,而且都是 DMA 控制器在干活,不占用 CPU

关键点:用户空间完全不参与,也看不到数据内容

6.3 为什么 RocketMQ 不用 sendfile

看一下两个函数的返回值:

c 复制代码
// mmap - 返回数据内容的指针
void *mmap(void *addr, size_t length, ...);

// sendfile - 返回发送了多少字节
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
  • mmap:应用层能拿到消息内容,可以做各种处理
  • sendfile:应用层只知道发了多少字节,不知道具体内容是什么

RocketMQ 的很多功能需要读取消息内容

  • 消息过滤:需要读取 Tag
  • 死信队列:需要知道是哪条消息失败了
  • 事务消息:需要根据消息内容做二次投递

如果用 sendfile,根本拿不到消息内容,这些功能就没法实现了。

所以

消息队列 零拷贝方案 原因
Kafka sendfile 追求极致性能,不需要读取消息内容
RocketMQ mmap 需要读取消息内容来实现丰富功能

这就是 RocketMQ 性能不如 Kafka 的根本原因。


七、如何选型

说了这么多,到底该选哪个?

7.1 选 Kafka 的场景

  • 大数据场景:日志采集、实时计算、数据管道

  • 关键词:Spark、Flink、Hadoop、数据湖、ETL

  • 特点:数据量巨大,对吞吐量要求极高,功能需求简单

    日志服务 → Kafka → Flink → 数据仓库
    用户行为 → Kafka → Spark → 实时分析

7.2 选 RocketMQ 的场景

  • 业务系统场景:电商、金融、订单、支付

  • 关键词:事务、延迟、重试、可靠性

  • 特点:功能需求复杂,对可靠性要求高,数据量相对可控

    订单服务 → RocketMQ → 库存服务
    支付服务 → RocketMQ(事务消息) → 积分服务

7.3 简单粗暴的选型标准

场景 推荐
能频繁听到 Spark、Flink 这些词 Kafka
其他业务场景 RocketMQ

八、总结

本文从架构、存储、功能、性能四个维度对比了 Kafka 和 RocketMQ:

维度 Kafka RocketMQ
元数据管理 Zookeeper(正在移除) NameServer(轻量)
存储模型 每个 Partition 存完整消息 CommitLog 统一存储
多 Topic 写入 可能退化为随机写 顺序写,性能更好
主从同步 按 Partition 同步 按 Broker 同步整个 CommitLog
功能丰富度 基础功能 消息过滤、事务、延迟、死信队列
零拷贝 sendfile mmap
吞吐量 ~17万/秒 ~10万/秒

核心结论

没有完美的架构,只有适合的场景。Kafka 追求极致性能,RocketMQ 追求功能丰富。做架构,做到最后,都是在做折中


热门专栏推荐

等等等还有许多优秀的合集在主页等着大家的光顾,感谢大家的支持

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😊

希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🙏

如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🌟

相关推荐
武子康2 小时前
Java-203 RabbitMQ 生产者/消费者工作流程拆解:Connection/Channel、默认交换器、ACK
java·分布式·消息队列·rabbitmq·erlang·ruby·java-rabbitmq
小满、3 小时前
RabbitMQ: 同步异步解析、安装与控制台实践
分布式·消息队列·rabbitmq·mq
社恐的小马同学3 小时前
RocketMQ: 发送一条消息经历了什么
rocketmq
yumgpkpm4 小时前
Cloudera CDP7、CDH5、CDH6 在华为鲲鹏 ARM 麒麟KylinOS做到无缝切换平缓迁移过程
大数据·arm开发·华为·flink·spark·kafka·cloudera
金海境科技4 小时前
【服务器数据恢复】数据中心私有云Ceph分布式集群文件丢失数据恢复案例
服务器·经验分享·分布式·ceph
音符犹如代码4 小时前
ZooKeeper 实战指南:从入门到场景解析
分布式·微服务·zookeeper·云原生·中间件·架构
树下水月4 小时前
Easyoole 使用rdkafka 进行kafka的创建topic创建 删除 以及数据发布 订阅
分布式·kafka
Cat God 0074 小时前
基于Docker搭建kafka集群
docker·容器·kafka
Cat God 0075 小时前
基于 Docker 部署 Kafka(KRaft + SASL/PLAIN 认证)
docker·容器·kafka