#DevOps

RabbitMQ vs Kafka vs Redis Streams:消息队列选型

"消息队列"不是一种东西——RabbitMQ(传统 AMQP,路由灵活)、Kafka(分布式日志,高吞吐流处理)、Redis Streams(轻量流),设计哲学完全不同。本文讲清三者的核心模型、各自擅长的场景,以及"按需求选而非按热度选"。

本文要点

  • 主题聚焦:DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年2月19日·更新于 2026年7月23日

RabbitMQ vs Kafka vs Redis Streams:消息队列选型

每次架构评审提到"加个消息队列",几乎必然出现一个争论:用 Kafka 还是 RabbitMQ?最近几年又多了一个选项:Redis Streams。争到后面往往发现,争的人其实没分清这三者根本不是同一类东西——只是恰好都叫"消息队列"。

写这篇文章的目的不是站队。三者在各自的领域都是一等一的好工具,关键在于:你的问题是不是它们各自擅长解决的那个问题。下面把三者的核心模型讲透,再讲怎么按需求选。

一、先搞清楚:消息队列根本不是一种东西

很多人把这个概念混在一起,但如果你只读一段话,请读这段:

系统本质消息生命周期
RabbitMQ队列 + 交换器(智能路由)消费确认后删除
Kafka分布式提交日志(append-only log)按策略保留,可重复消费
Redis Streams内存里的轻量日志流按策略保留(但受内存约束)

这三句话决定了后面所有差异:

  • RabbitMQ 是消息分发器。一条消息送进去,被路由到某个队列,一个消费者拿走、确认,然后这条消息就消失了。它的核心是"把这条消息可靠地交给该收的人"。
  • Kafka 是日志。生产者往日志末尾追加,消费者各自维护一个"读到哪里"的游标(offset),可以重播、可以倒退、可以多个消费者各读各的。消息不会因为"被读过"就消失。
  • Redis Streams 是介于两者之间的轻量流,数据结构上更像 Kafka(也是 append-only log + 消费者组),但跑在 Redis 内存里,没有 Kafka 那套重量级生态。

理解这个差异之后,下面所有的设计选择(路由、ACK、分区、保留策略)都不是凭空来的,都是从这三个本质里长出来的。

二、RabbitMQ:经典 AMQP,路由的艺术

2.1 核心模型:Exchange + Queue

RabbitMQ 的灵魂是解耦路由逻辑。生产者不直接把消息塞进队列,而是发给 Exchange;Exchange 根据规则把消息投到一个或多个 Queue。中间这个"根据规则"就是 RabbitMQ 最强的能力。

四种 Exchange 类型,记牢就够用了:

  • direct:按 routing_key 精确匹配。比如 routing_key = "error",只有绑定 error 的队列收到。
  • topic:按模式匹配,routing_key 支持通配符。logs.*.error 匹配 logs.app.error、logs.db.error。这是用得最多的一种。
  • fanout:广播。消息原样发给所有绑定的队列,忽略 routing_key。适合"一条事件多个系统都要处理"。
  • header:按消息 header 匹配,不依赖 routing_key。实际项目里少见,但偶尔在复杂路由条件下能救命。

举个典型场景:订单系统发一条 order.created 事件,库存服务、积分服务、推送服务、风控服务都要消费。在 RabbitMQ 里就是:一个 topic exchange,每个服务各起一个队列,按需绑定 order.# 或 order.created 这种 key。互不干扰,各自独立 ACK,各自重试。

2.2 ACK、重试、死信队列:可靠投递的护城河

RabbitMQ 对"消息一定要被正确处理"这件事非常认真:

  • 手动 ACK:消费者拿到消息后,处理完了才发 basic_ack。处理中进程崩了?没 ACK,消息会被重新投递给其他消费者。
  • ** prefetch**:限制单个消费者未 ACK 的消息数,避免一个消费者被压垮、其他消费者闲死。
  • 死信队列(DLX):消息被 basic_reject / basic_nack 且不重新投递、TTL 过期、队列满,都可以走死信路由到指定 exchange。异常消息集中处理,不会丢。
  • 延迟队列:通过 TTL + DLX 实现,过去是经典玩法(现在 RabbitMQ 也有官方的 rabbitmq_delayed_message_exchange 插件,更直接)。

这套机制让 RabbitMQ 在任务分发、订单处理、支付回调这类"每条消息都不能丢、都要有结果"的场景里几乎无对手。

2.3 RabbitMQ 的适用场景

  • 任务队列:Worker pool、Celery、Sidekiq 这类后台任务
  • 事件通知:业务事件 fanout 给多个订阅者
  • RPC:reply-to 队列 + correlation_id,做异步 RPC
  • 复杂路由:多种消息类型、按规则分发到不同消费者
  • 传统微服务通信:需要可靠投递、ACK、重试

不擅长:消息需要被反复消费(重播)、超大吞吐的日志流(百万 TPS 量级)、长时间的事件溯源。

2.4 部署与运维

  • Erlang 写的,分布式靠 clustering + 镜像队列(现在推荐 Quorum Queue,基于 Raft,比老的 mirror queue 更可靠)。
  • 单机性能几万到十几万 TPS,跨集群会进一步降。
  • 运维复杂度中等,CLI(rabbitmqctl)和 management UI 都很完善。

三、Kafka:分布式提交日志,为高吞吐而生

3.1 核心模型:Topic → Partition → Offset

Kafka 的核心抽象不是队列,是分区日志:

  • 一个 Topic 分成 N 个 Partition。
  • 每个 Partition 是一个有序的、不可变的、不断追加的日志。
  • 消息在 Partition 内有序,跨 Partition 无序。
  • 每条消息有一个 monotonically increasing 的 offset。
  • 消费者自己维护 offset,想读到哪读到哪,想倒回去重读就倒回去重读。

这个模型的关键后果:消费完全和生产的"删除"解耦。生产者只管往末尾写,消费者各自维护 offset,互不影响。一条消息可以被 N 个不同的消费者组各消费一次,彼此不知道对方存在。

3.2 消费者组:扩展与并行

Consumer Group 是 Kafka 的精髓:

  • 同一个 Group 内的消费者均分所有 Partition(一个 Partition 只被组内一个消费者消费)。
  • 想提高消费并行度?加 Partition、加消费者。但消费者数 > Partition 数时,多出来的消费者是废的。
  • 不同 Group 之间互不影响,各自独立消费完整数据。

这套机制让 Kafka 天生适合"一份事件流,被 N 个下游系统各自消费"——也就是事件驱动架构 / 数据中台的核心需求。

3.3 消息保留:这才是 Kafka 真正的杀手锏

RabbitMQ 是消费即删除,Kafka 是默认按时间保留 7 天(可配置),消费者有没有消费都无所谓。

这个差异看似简单,实际上彻底改变了你能做的事:

  • 事件回放:上线了一个新的消费者,从一周前的 offset 开始消费,把历史数据全量灌进来重新计算。这在 RabbitMQ 里做不到。
  • 故障恢复:消费者挂了 3 小时后恢复,从上次 commit 的 offset 接着读,没有数据丢失。
  • 事件溯源(Event Sourcing):把所有状态变更作为事件存下来,任何时候可以重建当前状态。
  • 流处理:Kafka + Streams / Flink / Spark Streaming,把消息当成流来计算窗口聚合、Join。
  • 数据管道(ETL):Kafka Connect 把数据库 binlog、日志、指标全灌进 Kafka,下游随便消费。

3.4 性能与可靠性

  • 顺序磁盘写 + 零拷贝(sendfile),单机轻松几十万到上百万 TPS。
  • 副本(Replica)+ ISR(In-Sync Replicas)机制保证持久性。
  • 通过 idempotent producer + 事务 可以做到 exactly-once 语义(成本是性能)。
  • 顺序保证只在 Partition 内,要全局有序就只有 1 个 Partition(吞吐就受限),这是 Kafka 一个固有的权衡。

3.5 Kafka 的适用场景

  • 日志聚合:应用日志、访问日志、审计日志
  • 事件流 / 流处理:实时数仓、用户行为分析、风控
  • 数据管道(ETL):CDC、数据同步
  • 事件溯源 / CQRS:需要回放、需要重建状态
  • 高吞吐消息总线:微服务之间的事件骨干

不擅长:复杂的路由规则(Kafka 没有 exchange 这种东西,路由靠 Topic 命名约定)、需要严格每条消息可靠投递 + 重试 + 死信的任务队列(要做的话非常繁琐)、小项目(运维成本不划算)。

3.6 部署与运维

  • 重。Zookeeper / KRaft、Broker、多副本、监控(Burrow、Kafka Exporter)、Schema Registry、Connect……一套下来就是一个团队。
  • 现在 KRaft 模式(移除 Zookeeper)让部署简化不少,但仍然是企业级重量。
  • 云上的 Managed Kafka(MSK、Confluent Cloud、Upstash)能省很多运维负担。

四、Redis Streams:藏在 Redis 里的轻量流

4.1 它是什么

Redis 5.0 引入的 Streams 数据结构,本质上是一个内存里的 append-only log + 消费者组模型。API 上很像 Kafka:

  • XADD 写入一条消息,自动生成 <timestamp>-<seq> 形式的 ID。
  • XREAD / XREADGROUP 消费。
  • XGROUP CREATE 建消费者组。
  • XPENDING / XCLAIM 处理游离消息(某个消费者挂了,它没 ACK 的消息被接管)。
  • XACK 确认。
  • XLEN、XRANGE、XREVRANGE 随机读取、回放。
  • MAXLEN / MINID 限制流长度,做 Trim。

看出来了吗?这就是一个迷你 Kafka——同样的 log + consumer group 模型,只是跑在 Redis 内存里。

4.2 它和 Kafka 的关键差异

维度KafkaRedis Streams
存储磁盘日志Redis 内存(可 RDB/AOF 持久化)
单条大小适合 KB 级别受限于内存,慎放大消息
吞吐百万级 TPS单机几十万 TPS(受内存带宽限制)
保留默认按时间/大小长期保留受内存约束,必须 Trim
集群扩展原生分区 + 副本Redis Cluster 分片( Streams 不能跨 slot)
运维成本重极低(如果已有 Redis)

4.3 它和 RabbitMQ 的关键差异

Redis Streams 没有 exchange / routing。如果你需要按规则把消息投到不同队列,RabbitMQ 是更好的选择。Redis Streams 的"路由"基本只能靠"不同 stream 名"来手动模拟。

4.4 Redis Streams 的适用场景

  • 中小项目的事件流:用户在线状态、IM 消息、实时计数
  • 跨进程的 Pub/Sub + 消息留存:Redis 原生的 Pub/Sub 断线即丢,Streams 解决了这个问题
  • 轻量任务队列:不想再起一个 RabbitMQ 的话,Streams + 消费者组能搞定
  • 缓存预热 / 实时排行榜这种 Redis 重度用户:复用已有 Redis,省运维

不擅长:大数据量、需要长期保留的事件流(内存放不下);对持久性要求极高、绝对不能丢的场景(Redis 默认异步持久化,崩溃可能丢最近几秒);复杂的路由规则。

4.5 一个常被忽略的点:Streams ≠ Redis Pub/Sub

Redis 早期的 Pub/Sub 是"在线才收"——订阅者没连着就丢消息,消息也不留存。Streams 的出现很大程度上是为了解决这个痛点。如果你的项目还在用 Pub/Sub 做关键事件分发,该换 Streams 了。

五、横向对比:一张表看清差异

维度RabbitMQKafkaRedis Streams
核心模型队列 + 交换器分布式提交日志内存日志流
消费后删除(ACK 后)保留,可重放保留,可重放(需 Trim)
路由能力极强(4 种 exchange)弱(靠 Topic 命名)无(靠 Stream 命名)
顺序保证单队列内有序单 Partition 内有序单 Stream 内有序
吞吐数万 ~ 十几万 TPS百万级 TPS数十万 TPS
延迟低(毫秒级)中(依赖 batch)极低
消息保留不保留长(默认 7 天,可永久)短(受内存约束)
消费者扩展加队列、加消费者加 Partition、加消费者加消费者(单 Stream 无 Partition)
可靠投递强(ACK + DLX)强(副本 + ISR)中(取决于持久化配置)
重放能力无强强
运维复杂度中高低(已有 Redis 时几乎为 0)
学习曲线中较陡平
典型场景任务队列、RPC、事件通知日志、流处理、ETL、事件溯源轻量事件流、复用 Redis 的项目

六、选型决策:按需求,不按热度

把上面所有内容浓缩成一个决策树:

1. 你的需求是"任务分发" / "可靠投递" / "复杂路由" 吗?

比如:订单处理、邮件发送、支付回调、Worker pool、跨服务 RPC、一条事件要按规则分给不同消费者。

→ 选 RabbitMQ。它的 exchange、ACK、DLX、prefetch 都是为这种场景生的。Kafka 也能做,但会非常别扭(你得自己实现重试、死信、延迟队列)。

2. 你的需求是"高吞吐事件流" / "日志" / "需要重放" 吗?

比如:用户行为日志、监控指标、风控事件流、实时数仓、CDC 数据管道、事件溯源、多个下游各自消费一份完整数据。

→ 选 Kafka。这是它无可替代的领域。重放、多消费者组、百万级吞吐、长期保留,是 RabbitMQ / Redis Streams 给不了的。

3. 你的需求是"轻量事件流" / "项目已经在用 Redis" / "不想引入新中间件" 吗?

比如:IM 消息、在线状态、中小型应用的实时通知、用 Redis 重度缓存的 Web 应用。

→ 选 Redis Streams。一个数据结构搞定消息流,运维成本几乎为零。

4. 你都不确定? 那就先回答一个问题:你需要重放消息吗?

  • 需要重放 → 在 Kafka 和 Redis Streams 之间选(按规模和已有基础设施)
  • 不需要重放,每条消息处理完就该消失 → RabbitMQ

七、别为了"用 Kafka"而上 Kafka

这一节单独拿出来,因为我见过太多团队踩这个坑。

Kafka 在技术圈的名气很大,以至于很多时候变成了一种"政治正确"——一上微服务、一上事件驱动架构,第一反应就是"上 Kafka"。但 Kafka 的成本是非常真实的:

  • 运维成本:一套 Kafka 集群(含 Zookeeper / KRaft、监控、Connect、Schema Registry)的运维负担,相当于多养半个中间件团队。
  • 复杂度:Partition 数怎么定?副本数怎么定?保留多久?消费者 rebalance 怎么处理?Offset 怎么 commit?这些都要搞清楚,不然线上一定会出事。
  • 小吞吐下的性价比低:如果你的场景是几千 TPS,RabbitMQ 一台机器就够了,Kafka 集群反而是过度设计。

我见过一个团队,把订单状态变更这种典型的任务分发场景(每条消息都要可靠投递、要重试、要死信)硬塞进 Kafka,结果为了实现"消费失败重试 + 死信"绕了一大圈(用 retry topic + dead letter topic 模式),代码极其啰嗦,bug 不断。如果一开始就用 RabbitMQ,半天的事。

反过来我也见过:一个日志聚合场景,每天 TB 级数据,团队用 RabbitMQ 跑了半年,集群天天抖,最后迁到 Kafka 一片坦途。

两边的坑都源于同一个错误:选型的时候看的是工具的"名气",而不是自己的"问题"。

所以最后一句话:

选消息队列,先想清楚你要解决的是什么问题。任务分发找 RabbitMQ,高吞吐事件流找 Kafka,轻量场景用 Redis Streams。别让工具的名气替你做架构决策。

小结

  1. 三者本质不同:RabbitMQ 是"队列+交换器"做消息分发(消费即删),Kafka 是"分布式提交日志"做事件流(保留可重放),Redis Streams 是"内存轻量流"(迷你版 Kafka)。它们的差异是设计哲学层面的,不是配置层面的。

  2. 选型的核心问题:你要重放吗? 需要重放、多消费者组各自消费完整流 → Kafka 或 Redis Streams。不需要重放、要可靠投递和复杂路由 → RabbitMQ。

  3. RabbitMQ 的护城河是路由和可靠投递:四种 exchange、ACK、死信、prefetch。任务队列、RPC、事件通知这种场景,它至今没有对手。

  4. Kafka 的护城河是高吞吐 + 重放:百万级 TPS、按时间保留、多消费者组、Partition 并行。日志、流处理、ETL、事件溯源这种场景,无可替代。

  5. Redis Streams 是"够用且省事"的选择:已经有 Redis、规模不大、需要轻量事件流——选它,省一个中间件。

金句:选消息队列,看的是你的问题长什么样,不是看哪一个在简历上写出来好看。Kafka 不是 RabbitMQ 的升级版,它们是两种东西;Redis Streams 是第三个选项,不是 RabbitMQ 的廉价替代品。

Related

相关推荐