注:该文用于个人学习记录和知识交流,如有不足,欢迎指点。
Redis 发布订阅(Pub/Sub)是 Redis 提供的一套消息通信机制,并非独立的数据结构(基于 Redis 内部机制实现,不依赖之前提到的 5 大基础结构)
一、Redis 发布订阅(Pub/Sub)核心概念
- 发布者(Publisher) :发送消息的一方,无需知道是否有订阅者存在,仅负责将消息发送到指定频道(Channel)或模式(Pattern)。
- 订阅者(Subscriber):接收消息的一方,可订阅一个或多个频道 / 模式,仅能接收自己订阅的频道 / 模式的消息,处于阻塞状态等待消息推送。
- 频道(Channel):消息的传递载体,是一个命名的消息通道,发布者向频道发消息,订阅该频道的所有订阅者都会收到消息(一对一多的广播)。
- 模式(Pattern) :支持通配符的频道匹配规则(如
news:*匹配news:sports、news:tech等所有以news:开头的频道),方便订阅者批量订阅一类频道。
二、核心操作指令
Redis 发布订阅的指令分为订阅者指令 、发布者指令 、辅助指令三类,其中订阅者执行指令后会进入阻塞状态,仅能接收消息或执行少量专属指令(如取消订阅)。
1. 订阅者核心指令(阻塞态)
| 指令 | 语法格式 | 指令说明 |
|---|---|---|
| SUBSCRIBE | SUBSCRIBE channel1 [channel2 ...] |
订阅一个或多个指定名称的频道,进入阻塞状态,后续仅能接收该频道的消息和自身状态变更通知 |
| PSUBSCRIBE | PSUBSCRIBE pattern1 [pattern2 ...] |
订阅一个或多个通配符模式 ,匹配所有符合模式的频道,如 PSUBSCRIBE app:* 可接收 app:login、app:register 的消息 |
| UNSUBSCRIBE | UNSUBSCRIBE [channel1 ...] |
取消订阅一个或多个指定频道,不指定参数则取消所有已订阅的普通频道 |
| PUNSUBSCRIBE | PUNSUBSCRIBE [pattern1 ...] |
取消订阅一个或多个指定模式,不指定参数则取消所有已订阅的模式 |
| PING | PING |
订阅者阻塞状态下可执行,用于测试与 Redis 服务器的连接,服务器返回 PONG |
注意:
阻塞态下的可操作边界
- 支持的操作 :
仅能执行「订阅 / 取消订阅」相关指令(SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE、PUNSUBSCRIBE),以及心跳测试指令PING,这些指令执行后不会退出阻塞态。- 不支持的操作 :
无法执行GET、SET、HSET等普通数据操作指令,这些指令在阻塞态下执行会报错。
2. 发布者核心指令(非阻塞态)
| 指令 | 语法格式 | 指令说明 |
|---|---|---|
| PUBLISH | PUBLISH channel message |
向指定单个频道发送一条消息,返回值为接收该消息的订阅者数量(包括普通订阅和模式匹配订阅) |
3. 辅助指令(查询订阅状态)
| 指令 | 语法格式 | 指令说明 |
|---|---|---|
| PUBSUB CHANNELS | PUBSUB CHANNELS [pattern] |
查询当前 Redis 中存在的所有频道,指定 pattern 可过滤符合规则的频道(如 PUBSUB CHANNELS news:*) |
| PUBSUB NUMSUB | PUBSUB NUMSUB [channel1 ...] |
查询指定一个或多个频道的订阅者数量(仅统计普通订阅,不包含模式订阅) |
| PUBSUB NUMPAT | PUBSUB NUMPAT |
查询当前 Redis 中模式订阅的总数量(即有多少个活跃的模式匹配规则) |
三、典型应用场景
Redis 发布订阅是轻量级、低耦合的消息通信方案,适合对消息可靠性要求不高、追求简单快速部署的场景,典型落地场景如下:
1. 实时消息推送
- 场景:系统通知(如用户注册成功通知、订单状态变更提醒)、公众号 / 小程序实时推送、聊天室消息广播。
- 实现:订阅者(前端客户端 / 后端服务)订阅
notify:user:1001(用户 1001 的专属通知频道),发布者(业务服务)在订单状态变更时执行PUBLISH notify:user:1001 "您的订单已发货",订阅者实时接收并展示消息。
2. 分布式系统配置变更同步
- 场景:微服务架构中,配置中心修改配置后,同步通知所有相关服务刷新本地配置缓存,无需服务重启。
- 实现:所有微服务订阅
config:refresh频道,配置中心修改配置后,向该频道发布{"key":"app.port","value":8081},微服务接收消息后自动刷新本地配置。
3. 日志集中收集
- 场景:多台应用服务器产生的日志,实时推送至 Redis 频道,由专门的日志收集服务(如 ELK 客户端)订阅该频道,统一收集、解析、存储日志。
- 实现:每台应用服务器作为发布者,向
log:collect频道发布日志消息(如PUBLISH log:collect "2026-01-13 10:00: [INFO] 接口调用成功"),日志收集服务订阅该频道,实时获取并处理日志。
4. 轻量级广播通知
- 场景:电商平台秒杀活动开始前,向所有关注该活动的用户发送预热通知;游戏服务器向所有在线玩家发送全服公告。
- 实现:用户关注活动时订阅
activity:seckill:1001频道,活动预热阶段,平台向该频道发布广播消息,所有订阅用户实时接收。
四、Redis 发布订阅的局限性(重要)
Redis Pub/Sub 是轻量级方案,不适合对消息可靠性、持久性有严格要求的场景,核心局限性如下:
- 无消息持久化机制:Redis 不会存储发布的消息,若订阅者离线(未在线接收),则会永久丢失该期间的消息,且消息发送后立即被丢弃,无法回溯历史消息。
- 无消息确认与重试机制:发布者发送消息后,Redis 仅负责转发,不保证订阅者是否成功接收、处理消息,无重试、重传机制。
- 无消息堆积能力:若订阅者处理消息的速度慢于发布者发送消息的速度,Redis 不会缓存未处理的消息,会直接丢弃超出处理能力的消息(无队列堆积)。
- 集群环境下支持有限:Redis 集群中,Pub/Sub 消息仅在当前节点转发,不会跨节点同步,若订阅者和发布者连接不同节点,可能无法接收消息。
- 不支持消息过滤与优先级:无法对消息进行精细化过滤,也不支持消息优先级排序,所有消息均按发送顺序转发。
五、与 List 实现的消息队列的区别
很多人会混淆 Pub/Sub 和 List 实现的消息队列,两者核心差异如下:
| 特性 | Redis Pub/Sub | Redis List(LPUSH + BRPOP) |
|---|---|---|
| 消息模型 | 发布 - 订阅(广播),一对多 | 生产者 - 消费者(队列),一对一 / 一对多(竞争消费) |
| 消息持久化 | 无,不存储消息 | 有,消息存储在 List 中,未被消费前不会丢失 |
| 离线消息支持 | 不支持,订阅者离线丢失消息 | 支持,消费者离线后,消息仍在 List 中,上线后可继续消费 |
| 消息堆积 | 不支持,无堆积能力 | 支持,消息可堆积在 List 中(需限制列表长度避免内存溢出) |
| 适用场景 | 实时广播、低可靠性要求的通知 | 任务队列、消息异步处理、对可靠性有一定要求的场景 |
六、细节补充
1. 订阅机制的底层逻辑
底层是 "订阅时存关系 → 发布时找关系 → 找到后转发消息" 的闭环,转发是最终执行动作,但依赖前序的存储和匹配:
1.1 订阅阶段:存储 "客户端 - 订阅对象" 的关联关系
当客户端执行 SUBSCRIBE(普通频道)或 PSUBSCRIBE(模式)时,Redis 会在内存中维护两套独立的字典结构:
- 普通频道字典:
key=频道名,value=订阅该频道的客户端列表- 模式字典:
key=模式规则,value=订阅该模式的客户端列表(客户端断开连接后,Redis 会自动从这些字典中移除对应的订阅关系)
1.2 发布阶段:匹配订阅关系 + 执行消息转发
当发布者执行 PUBLISH channel message 时,Redis 会做两步匹配:
- 第一步:精准匹配普通频道字典,找到订阅该
channel的所有客户端,直接转发消息;- 第二步:遍历模式字典,筛选出能匹配该
channel的模式规则(如channel=news:sports匹配模式news:*),找到这些模式对应的客户端,再转发消息;(若两步都没找到客户端,消息直接被丢弃)
2. 普通频道跟模式是分开管理的
很多人会有误区:「向普通频道发消息,模式订阅者也能收到,是不是二者有关联?」其实不是,消息转发只是上层的 "匹配逻辑",底层的订阅记录依然是两个独立的集合,具体流程如下:
- 发布者向
news:sports(普通频道)发送消息;- Redis 首先遍历「普通频道订阅列表」,向所有订阅
news:sports的客户端转发消息;- 随后,Redis 遍历「所有活跃模式列表」,筛选出能匹配
news:sports的模式(如news:*);- 最后,遍历匹配到的模式对应的「模式订阅列表」,向所有订阅该模式的客户端转发消息;
- 整个过程中,两个订阅列表独立遍历、独立转发,互不干扰,即使其中一个列表为空,也不影响另一个列表的转发逻辑。
简单讲:
- Redis 对「普通频道」和「模式」是完全分开独立管理的,拥有独立的订阅列表、操作指令和状态统计。
- 核心特征:指令不互通、状态不互包含,仅在消息转发时通过 "模式匹配" 实现上层关联。
- 实操要点:订阅 / 取消订阅必须对应指令,查询状态需使用各自的统计指令,避免跨体系操作无效。
3. 必须要有人订阅才能publish吗?
- 发布者可以向任意频道自由发布消息,不受订阅者存在与否的限制;
- 无人订阅时,消息会被立即丢弃,无法回溯;
PUBLISH的返回值可直观判断是否有订阅者接收消息。
七、总结
- Redis Pub/Sub 是轻量级广播通信机制,非独立数据结构,部署简单、低耦合,适合实时通知、日志收集等低可靠性要求场景。
- 核心指令需区分订阅者(阻塞态,
SUBSCRIBE/PSUBSCRIBE)和发布者(非阻塞态,PUBLISH)。 - 其最大短板是无持久化、无确认机制,不适合金融交易、订单处理等对消息可靠性要求高的场景,此类场景建议使用专业消息队列(如 RabbitMQ、Kafka)。