为什么需要消息队列?
1. 系统解耦(最核心目的)
传统同步调用:A 服务直接调用 B、C、D 服务,接口强依赖,任意下游宕机、改接口都会导致 A 报错,代码耦合严重。 引入 MQ 后:生产者只发送消息到队列,无需关心谁消费、如何处理;消费者自主拉取消息,上下游完全隔离。 新增 / 删除下游业务,生产者代码零改动。
2. 削峰填谷,抗流量突发
秒杀、活动大促、定时任务瞬间产生海量请求,数据库、接口瞬时并发压垮,直接超时、崩溃。
- 峰值:流量全部存入消息队列,不会直接冲击底层服务;
- 谷值:消费端匀速慢慢处理堆积消息,平稳消化流量,保护数据库、接口不被打崩。
3. 异步处理,提升主接口响应速度
很多业务存在非实时附属操作:下单后发短信、发优惠券、日志记录、物流通知、数据统计。 同步方案:用户下单要等短信、统计全部执行完才返回,接口耗时高,用户等待卡顿。 MQ 异步方案:下单主逻辑完成立刻返回结果,后续附属任务丢进队列后台异步执行,大幅缩短主流程 RT,优化用户体验。
4. 最终一致性,保证可靠事务
分布式多服务场景下,同步调用容易出现部分成功、部分失败的数据不一致问题(下单成功但库存扣减失败)。 利用 MQ 可靠投递、死信队列、事务消息,实现分布式事务兜底,保证多服务业务要么全部完成,要么补偿回滚,避免数据错乱。
5. 消息复用,多消费广播
一条业务消息可被多个消费者同时消费:订单创建消息,订单服务、积分服务、推送服务、数据分析服务各自监听,一份数据源支撑多业务,不用重复生产、重复查询数据库。
Redis消息队列?
在秒杀、异步通知、解耦削峰等业务场景中,消息队列是核心基础设施。如果项目已经重度依赖 Redis,无需额外部署 RocketMQ、RabbitMQ 等专业中间件,可直接基于 Redis 原生数据结构快速实现简易消息队列。
Redis 提供了三套原生消息模型:双向链表 List、发布订阅 PubSub、流式数据结构 Stream。三者底层实现、适用场景、可靠性差距极大,本文结合底层原理、优缺点、业务落地场景完整拆解对比。
一、基于 List 双向链表实现消息队列
1. 底层实现原理
Redis List 是双向链表,支持两端读写,天然满足队列「先进先出」特性。
- 生产者入队:
LPUSH queue msg(左侧写入消息) - 消费者出队:
RPOP queue(右侧读取消息) - 阻塞等待优化:普通
RPOP无消息直接返回nil,需循环轮询浪费 CPU;使用BRPOP queue timeout实现阻塞读取,无消息时线程挂起,有消息自动唤醒。

2. 核心优势
- 支持持久化 List 属于普通 Redis Key,开启 RDB/AOF 持久化后,消息落盘存储,Redis 宕机重启消息不会丢失。
- 不受 JVM 内存限制 消息存储在 Redis 服务端内存,区别于 Java 内置阻塞队列,集群多实例可共用同一队列。
- 天然有序 链表按入队顺序存储,严格保证消息 FIFO 顺序,适合有序处理场景。
3. 缺点
- 仅支持单消费者 一条消息执行
RPOP后会直接从链表删除,多个消费者竞争队列时,消息只会被其中一个线程消费,无法实现多消费者并行消费;若要多实例扩容,只能手动拆分多个队列。 - 无消息 ACK 确认机制 消息一旦弹出队列就永久删除,若消费者读取消息后宕机,业务逻辑未执行完成,消息直接丢失,无法重试。
- 无法回溯历史消息 消息消费完成即删除,不能重新读取历史消息,排查故障、数据补偿场景受限。
4. 适用场景
低并发、单实例消费、允许少量消息丢失的轻量异步场景,例如简单日志同步、本地缓存刷新通知。
二、基于 PubSub 发布订阅模型实现消息队列
1. 底层实现原理
Redis 2.0 推出发布订阅机制,采用一对多广播模型:
- 生产者:
PUBLISH channel msg向指定频道推送消息 - 消费者:
SUBSCRIBE channel订阅频道,频道消息会推送给所有订阅客户端 - 模式匹配:
PSUBSCRIBE order.*模糊匹配多个频道,实现通配订阅
一条消息发布后,所有在线订阅者都会收到完整消息,属于广播模式。

2. 核心优势
- 原生多生产者、多消费者广播 一条消息可同时下发给多个消费者,适用于通知同步场景(如订单完成后同步通知积分、物流、短信多个服务)。
- 天然阻塞等待 订阅后客户端持续阻塞,频道有消息实时推送,无需手动轮询。
3. 缺点
- 完全不支持持久化 PubSub 消息仅存在 Redis 瞬时缓冲区,不落地磁盘。消费者离线期间生产者发送的消息会直接丢弃,重启后无法补收离线消息。
- 无消息堆积能力 消费者消费速度跟不上生产速度时,缓冲区溢出会直接丢弃消息;没有持久存储层做消息缓冲。
- 无 ACK 确认、消息丢失风险高 消息推送完成即丢弃,消费者宕机、网络抖动都会丢失消息,无法重试。
4. 适用场景
实时广播通知、对消息可靠性无要求的实时推送场景,例如在线状态推送、实时前端消息通知,不适合订单、支付等核心业务。
三、基于 Stream 流式结构实现消息队列
1. 底层实现原理
Stream 是 Redis 5.0 专为消息队列设计的数据结构,底层是有序日志流,每条消息携带唯一自增 ID 永久存储,配套消费者组 Consumer Group 实现可靠消费。 核心配套命令:
- 生产者写入:
XADD queue * k1 v1写入消息,自动生成唯一 ID - 阻塞消费:
XREADGROUP GROUP g1 c1 BLOCK 0 STREAMS queue >消费者组阻塞读取消息 - 消息确认:
XACK queue g1 msgId业务处理完成后手动确认消息 - 消息回溯:可通过指定历史 ID,重新读取任意历史消息
2. 消费者组三大核心能力
(1)消息分流(多消费者并行消费)
同一个消费者组内多个消费者监听同一 Stream,消息会分摊给组内不同消费者,一条消息仅被一个消费者处理,实现消费扩容,提升处理速度。
(2)消费位点持久化记录
消费者组会持久记录当前消费到的消息 ID,服务宕机重启后,会从上次未处理完成的消息继续读取,不会漏消费。
(3)Pending-List 消息确认机制
消费者读取消息后,消息会存入待确认列表(Pending-List),不会直接删除;必须手动执行 XACK 标记处理完成,才会移除待确认状态。 若消费者宕机、超时未确认,其他消费者可接管该消息重试,保证消息至少被消费一次。
3. 核心优势
- 支持持久化存储 Stream 消息完整落盘,Redis 重启不丢失历史消息,支持配置消息过期时间控制存储容量。
- 完备 ACK 确认机制,可靠性强 待确认列表 + XACK 手动确认,杜绝消费宕机导致的消息丢失,支持消息重试。
- 支持消息回溯 可指定任意历史消息 ID 读取日志,故障排查、数据补偿场景友好。
- 阻塞读取 + 多消费者扩容 支持阻塞等待消息;消费者组实现负载均衡,多实例并行消费,解决消息堆积。
4. 缺点
- 架构复杂度高于 List、PubSub,需要维护消费者组、手动处理 ACK、超时未确认消息;
- 存在消息重复消费可能(至少一次语义),业务侧需要做幂等处理。
5. 适用场景
生产级可靠异步场景,秒杀削峰、订单异步处理、优惠券发放、数据同步等核心业务,是 Redis 实现消息队列的最优方案。
四、三种方式对比
| 对比维度 | List | PubSub | Stream |
|---|---|---|---|
| 消息持久化 | 支持 | 不支持 | 支持 |
| 阻塞读取 | BRPOP 支持 | SUBSCRIBE 原生阻塞 | XREADGROUP 支持 |
| 多消费者并行消费 | 不支持,单消费 | 广播模式,全部接收 | 消费者组分流,负载均衡 |
| ACK 确认机制 | 无 | 无 | 有 XACK 确认、Pending 列表 |
| 消息回溯 | 不支持,消费即删除 | 不支持,离线消息丢失 | 支持,可读取任意历史消息 |
| 消息丢失风险 | 中等(宕机未处理丢失) | 极高(离线、堆积全部丢失) | 极低(至少消费一次) |
| 消息堆积能力 | 依赖 Redis 内存上限 | 缓冲区极小,极易丢消息 | 支持长期堆积,可配置过期 |
| 适用可靠性等级 | 低可靠非核心业务 | 实时广播、低可靠通知 | 高可靠核心异步业务 |
如何选择?
- 简单轻量异步、单实例、无可靠性要求 → List 队列 例如本地缓存刷新、简单日志异步打印,开发成本最低。
- 多服务实时广播通知、不在乎消息丢失 → PubSub 例如直播间在线人数推送、前端实时弹窗通知。
- 秒杀削峰、订单异步、支付回调、核心业务解耦 → Stream 消费者组 生产环境首选,兼顾持久化、可靠 ACK、多实例扩容,是 Redis 官方标准消息队列实现。