好的,根据你的需求,已经完成了 变清晰 操作。
你的团队正在开发一个电商小程序的 V1 版本。
架构师要求:“用户下单后,异步发送一封邮件,并给仓库推一条备货消息。去搭个消息队列吧。”
你的困境:
团队一共就 3 个后端,服务器资源紧巴巴。
用 Redis List 做 MQ?消费者一崩溃,消息就永远丢失了,不敢用。
用 Redis Pub/Sub?一旦消费者网络断开,期间的消息直接丢弃,更不敢用。
难道真的要去维护一套庞大复杂的 Kafka 集群吗?
破局之道:
祭出 Redis 5.0 的终极武器 —— Stream
它完美借鉴了 Kafka 的设计思想(消费组、持久化、ACK 确认),同时又极其轻量。只要你有 Redis,你就有了一个五脏俱全的 MQ。


1. 核心痛点:为什么 List 和 Pub/Sub 不能做靠谱的 MQ?

在 Stream 诞生之前,大家都在用 Redis 里的“偏方”做 MQ,但都伴随着剧痛:

  • Redis List (LPUSH / BRPOP) 的原罪:

  • 阅后即焚: 消息一旦被 BRPOP 弹出,就从 List 里消失了。如果消费者拿到消息后还没处理完就宕机了,这条消息就永久丢失了(虽然可以用 BRPOPLPUSH 抢救,但极度复杂)。

  • 无法多播: 一条消息只能被一个消费者吃掉,不支持发布/订阅(不能同时发给邮件服务和积分服务)。

  • Redis Pub/Sub (发布/订阅) 的原罪:

  • 没有记忆: 它是纯内存的 Fire-and-Forget。如果消费者离线了 5 分钟,这 5 分钟内的所有消息对它来说就像没发生过一样,不支持持久化,极不靠谱。

Redis Stream 的降维打击:
它把两者的优点结合了,并加入了类似 Kafka 的机制。它支持持久化支持多播,最重要的是,它引入了 ACK 确认机制


2. 深入底层:Stream 的三大硬核机制

Stream 为什么敢自称“靠谱”的 MQ?全靠以下三大机制的支撑。

机制一:Append-Only Log (追加日志) 与 Radix Tree

Stream 的底层结构是一棵基数树(Radix Tree),这使得它追加消息(XADD)的速度极快,并且消息是持久化存储在内存中的(配合 AOF/RDB 即可落盘)。每一条消息都有一个严格递增的全局唯一 ID(默认是 时间戳-毫秒内序号)。

机制二:Consumer Group (消费组) 的借鉴

这是 Kafka 的精髓,Redis 完美复刻了。

  • 你可以为同一个 Stream 创建多个消费组(比如 MailGroupStockGroup)。
  • 组间广播: 每个消费组都能收到完整的一份消息。
  • 组内负载均衡: 同一个消费组内的多个消费者(Consumer A, Consumer B),会竞争同一批消息,一条消息只会被组内的一个消费者处理。
机制三:PEL (Pending Entries List) 待处理列表机制

这是 Stream 解决“消息丢失”的绝杀!
当消费者从 Stream 中读取了一条消息,这条消息并不会被删除,而是进入了该消费者的 PEL (待处理列表) 中。

  • 只有当消费者处理完业务逻辑,明确向 Redis 发送一条 XACK 命令。
  • Redis 才会把这条消息从 PEL 中移除。
  • 容灾补救: 如果消费者中途宕机,消息依然留在 PEL 里。别的消费者可以通过 XPENDING 查出这些“僵尸消息”,并通过 XCLAIM 把消息的归属权抢过来,重新消费。

3. 三大高频实战场景

场景一:异步通知与任务解耦 (如:发短信/邮件)
  • 痛点: 用户注册接口,既要写库,又要调第三方发短信,如果短信 API 慢,会导致注册接口超时。
  • Stream 方案:
  1. 业务系统:注册成功后,执行 XADD register_stream * user_id 10086。接口瞬间返回。
  2. 短信服务(组 sms_group):一直阻塞读取 XREADGROUP ... BLOCK 0 ...,读到消息去发短信,发完执行 XACK
  3. 邮件服务(组 mail_group):同样读取,互不干扰。
场景二:活动操作日志 / 审计追踪 (顺序事件流)
  • 痛点: 需要记录用户在系统里的一连串操作(登录 -> 点击商品 -> 加入购物车 -> 支付),并且必须保证严格的顺序性
  • Stream 方案: Stream 天生就是为时序数据设计的。它的 Message ID 1708930000000-0 自带绝对的时间顺序。用它来收集分布式系统的审计日志,前端随便打,后端统一用单个 Consumer 顺序消费写入 ES 或 MySQL,稳如老狗。
场景三:物联网 (IoT) 传感器数据缓冲
  • 痛点: 1 万个温度传感器每秒上报一次数据。如果直接怼进 MySQL,数据库瞬间爆炸。
  • Stream 方案: 把 Stream 当作大坝(Buffer)
  • 所有传感器疯狂 XADD
  • 后端跑几个消费者,批量拉取数据,每凑够 1000 条,再做一个批量 INSERT 写进时序数据库或 MySQL。极大削峰填谷。

4. 避坑指南:Stream 真的能彻底替代 Kafka 吗?

千万别上头! 虽然 Stream 很强,但它和 Kafka 的定位完全不同。如果你强行用 Stream 扛大数据量,会死得很惨。

  1. 内存危机 (OOM):
    Kafka 的消息是存在磁盘(PageCache)上的,可以存几个 TB 的历史数据。
    Redis Stream 的消息是存在内存里的! 如果你不限制长度,内存迟早被撑爆。
    实战对策: XADD 时一定要带上 MAXLEN 参数!
    XADD mystream MAXLEN ~ 100000 * key value (使用 ~ 表示近似截断,性能更好,队列永远维持在最多 10 万条左右)。
  2. 吞吐量天花板:
    Kafka 通过分片(Partition)机制,吞吐量可以无上限水平扩展。而 Redis Stream 依附于单个 Redis 节点(虽然有 Cluster,但单个 Stream key 只能存在于一个分片上),吞吐量受限于单机的网络 I/O 和 CPU(通常在 10w TPS 级别)。
  3. 消息积压灾难:
    如果消费者全挂了,生产者还在猛发,Stream 会导致 Redis 内存报警,进而引发内存淘汰策略错杀其他缓存数据。

5. 总结

在架构选型中,没有绝对的最好,只有最合适。

  • 当你的业务体量在千万级以下,需要一个绝对可靠的异步解耦方案,且不想引入额外的运维负担时,Redis Stream 是你的绝对首选。
  • 当你的业务涉及百亿级日志聚合、大数据流计算、海量历史消息回放时,请老老实实去用 Kafka 或 Pulsar。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐