上一篇我们聚焦Redis在电商核心场景的实战应用,通过商品缓存、库存扣减、分布式锁解决了高并发下的性能与数据一致性问题。而在电商、支付、日志同步等业务中,消息队列是解耦服务、异步削峰的关键组件,Redis凭借轻量、易用、高性能的特性,成为中小型业务和快速落地场景的首选MQ方案。
Redis提供了三种消息队列实现方案:List(列表)、Pub/Sub(发布订阅)、Stream(流),三者适用场景、可靠性、功能特性差异极大。本篇从核心原理、实战代码、优缺点分析到生产选型,全方位拆解三种方案,帮你在业务中精准选择合适的Redis消息队列。
核心定位:Redis MQ主打轻量快捷,无需部署独立MQ中间件,List适合简单队列,Pub/Sub适合广播场景,Stream是生产级可靠消息方案。
一、方案一:基于List实现简单消息队列
List是Redis基础数据结构,通过LPUSH(生产者入队)+ BRPOP(消费者阻塞出队)实现轻量级队列,是Redis最原始、最简单的MQ实现,适合无严格可靠性要求的简单异步场景。
1. 核心原理
- 生产者用
LPUSH将消息推入队列左侧,消费者用BRPOP阻塞式从队列右侧拉取消息 - 支持消息持久化(依赖Redis RDB/AOF),重启后消息不丢失
- 单消费者模式,多消费者会出现消息争抢,无法实现广播或分组消费
2. 实战代码(Java)
/**
* List队列Key
*/
private static final String LIST_MQ_KEY = "mq:list:order";
// 生产者:消息入队
public void listProducer(String msg) {
redisTemplate.opsForList().leftPush(LIST_MQ_KEY, msg);
}
// 消费者:阻塞拉取消息(后台线程持续监听)
@Async
public void listConsumer() {
while (true) {
// 阻塞超时时间:5秒,避免空轮询
String msg = redisTemplate.opsForList().rightPop(LIST_MQ_KEY, 5, TimeUnit.SECONDS);
if (StringUtils.isNotBlank(msg)) {
// 处理业务逻辑
handleMsg(msg);
}
}
}
3. 优缺点分析
- ✅ 优点:实现极简、性能极高、支持持久化、无额外依赖
- ❌ 缺点:不支持ACK确认、无消费重试、不支持分组/广播、消息无法重复消费
4. 适用场景
日志异步记录、简单通知推送、非核心业务异步处理,对消息可靠性要求低的场景。
二、方案二:基于Pub/Sub实现发布订阅模式
Pub/Sub是Redis专属的发布订阅模型,生产者(发布者)发送消息到频道,多个消费者(订阅者)可同时接收消息,实现广播消费,但完全不持久化,适合实时性高、允许消息丢失的场景。
1. 核心原理
- 发布者通过
PUBLISH向频道发送消息,订阅者通过SUBSCRIBE监听频道 - 消息不存储、不持久化,订阅者离线期间的消息会直接丢失
- 支持多订阅者广播,一个消息可被多个消费者同时消费
2. 实战代码(Java)
/**
* Pub/Sub频道名
*/
private static final String PUBSUB_CHANNEL = "mq:channel:notice";
// 生产者:发布消息
public void pubProducer(String msg) {
redisTemplate.convertAndSend(PUBSUB_CHANNEL, msg);
}
// 消费者:订阅频道(配置类注册监听器)
@Component
public class PubSubConsumer extends MessageListenerAdapter {
@Override
public void onMessage(Message message, byte[] pattern) {
String msg = new String(message.getBody());
// 处理广播消息
handleBroadcastMsg(msg);
}
}
3. 优缺点分析
- ✅ 优点:支持广播消费、延迟极低、实现简单、多消费者实时同步
- ❌ 缺点:无持久化、消息不可靠、离线丢消息、无ACK、不支持重试
4. 适用场景
实时通知、缓存刷新广播、配置推送、聊天室消息,允许少量消息丢失的实时场景。
三、方案三:基于Stream实现生产级可靠消息队列
Stream是Redis 5.0新增的专门用于消息队列的数据类型,借鉴了Kafka的设计理念,支持消息持久化、ACK确认、消费分组、偏移量重置、消息回溯,是Redis官方推荐的生产级MQ方案,完美弥补List和Pub/Sub的缺陷。
1. 核心特性
- 消息持久化存储,支持历史消息回溯
- 消费分组(Consumer Group),支持负载均衡和广播两种模式
- ACK确认机制,消费失败可重试,避免消息丢失
- 自动生成唯一消息ID,支持按偏移量消费
2. 实战代码(Java+消费分组)
/**
* Stream队列Key、消费组名
*/
private static final String STREAM_MQ_KEY = "mq:stream:order";
private static final String STREAM_GROUP = "order-group";
private static final String STREAM_CONSUMER = "order-consumer-01";
// 初始化消费组(项目启动时执行一次)
@PostConstruct
public void initStreamGroup() {
try {
redisTemplate.opsForStream().createGroup(STREAM_MQ_KEY, STREAM_GROUP);
} catch (Exception e) {
// 消费组已存在,忽略异常
}
}
// 生产者:发送消息
public void streamProducer(String orderNo, String userId) {
MapRecord<String, String, String> record = Record.of(Map.of("orderNo", orderNo, "userId", userId));
redisTemplate.opsForStream().add(STREAM_MQ_KEY, record);
}
// 消费者:分组消费+ACK确认
@Async
public void streamConsumer() {
while (true) {
// 阻塞拉取消息,每次拉取1条,超时5秒
List<MapRecord<String, String, String>> records = redisTemplate.opsForStream()
.read(Consumer.from(STREAM_GROUP, STREAM_CONSUMER),
StreamReadOptions.empty().block(Duration.ofSeconds(5)).count(1),
StreamOffset.create(STREAM_MQ_KEY, ReadOffset.lastConsumed()));
if (CollUtil.isNotEmpty(records)) {
for (MapRecord<String, String, String> record : records) {
try {
// 处理业务逻辑
handleOrderMsg(record.getValue());
// ACK确认:标记消息已消费
redisTemplate.opsForStream().acknowledge(STREAM_MQ_KEY, STREAM_GROUP, record.getId());
} catch (Exception e) {
// 消费失败,记录日志,等待重试
log.error("消息消费失败,ID:{}", record.getId(), e);
}
}
}
}
}
3. 优缺点分析
- ✅ 优点:生产级可靠、持久化、ACK确认、消费分组、消息回溯、负载均衡
- ❌ 缺点:相比List/Pub/Sub稍重、学习成本略高、低版本Redis不支持
4. 适用场景
订单异步处理、支付结果通知、库存同步、日志持久化采集,对消息可靠性有要求的核心业务。
四、三种Redis消息队列全方位对比
| 对比维度 | List(列表) | Pub/Sub(发布订阅) | Stream(流) |
|---|---|---|---|
| 持久化 | 支持(依赖RDB/AOF) | 不支持,离线丢消息 | 完整持久化,可回溯 |
| ACK确认 | 不支持 | 不支持 | 支持,消费更可靠 |
| 消费模式 | 单消费者,争抢模式 | 多消费者,广播模式 | 分组负载+广播双模式 |
| 消息丢失 | 极低(重启可恢复) | 极高,离线必丢 | 几乎不丢失 |
| 复杂度 | 极低 | 低 | 中等 |
| 生产适用性 | 简单非核心场景 | 实时广播场景 | 核心业务,生产首选 |
选型口诀:简单异步用List,实时广播选Pub/Sub,核心可靠上Stream,Redis MQ按需选,轻量快捷不复杂。
五、生产环境选型建议与避坑
1. 精准选型指南
- 中小型项目、非核心异步任务:选用List,快速落地
- 多服务广播通知、实时推送:选用Pub/Sub,极低延迟
- 核心业务、订单/支付/库存同步:选用Stream,生产级可靠
2. 生产避坑要点
- List禁止用于核心业务,无ACK机制存在消息丢失风险
- Pub/Sub严禁用于订单、支付等不能丢消息的场景
- Stream需合理设置消息过期时间,避免内存暴涨
- 高吞吐大流量场景,优先选用专业MQ(Kafka/RocketMQ)
结语
Redis消息队列是轻量异步解耦的利器,三种方案各有所长,没有绝对的优劣,只有场景的适配。掌握List、Pub/Sub、Stream的特性与选型,既能快速落地业务,又能避免消息丢失、性能瓶颈等问题。