Linux C/C++ 学习日记(62):Redis(三):订阅

注:该文用于个人学习记录和知识交流,如有不足,欢迎指点。

Redis 发布订阅(Pub/Sub)是 Redis 提供的一套消息通信机制,并非独立的数据结构(基于 Redis 内部机制实现,不依赖之前提到的 5 大基础结构)

一、Redis 发布订阅(Pub/Sub)核心概念

  1. 发布者(Publisher) :发送消息的一方,无需知道是否有订阅者存在,仅负责将消息发送到指定频道(Channel)模式(Pattern)
  2. 订阅者(Subscriber):接收消息的一方,可订阅一个或多个频道 / 模式,仅能接收自己订阅的频道 / 模式的消息,处于阻塞状态等待消息推送。
  3. 频道(Channel):消息的传递载体,是一个命名的消息通道,发布者向频道发消息,订阅该频道的所有订阅者都会收到消息(一对一多的广播)。
  4. 模式(Pattern) :支持通配符的频道匹配规则(如 news:* 匹配 news:sportsnews:tech 等所有以 news: 开头的频道),方便订阅者批量订阅一类频道。

二、核心操作指令

Redis 发布订阅的指令分为订阅者指令发布者指令辅助指令三类,其中订阅者执行指令后会进入阻塞状态,仅能接收消息或执行少量专属指令(如取消订阅)。

1. 订阅者核心指令(阻塞态)

指令 语法格式 指令说明
SUBSCRIBE SUBSCRIBE channel1 [channel2 ...] 订阅一个或多个指定名称的频道,进入阻塞状态,后续仅能接收该频道的消息和自身状态变更通知
PSUBSCRIBE PSUBSCRIBE pattern1 [pattern2 ...] 订阅一个或多个通配符模式 ,匹配所有符合模式的频道,如 PSUBSCRIBE app:* 可接收 app:loginapp:register 的消息
UNSUBSCRIBE UNSUBSCRIBE [channel1 ...] 取消订阅一个或多个指定频道,不指定参数则取消所有已订阅的普通频道
PUNSUBSCRIBE PUNSUBSCRIBE [pattern1 ...] 取消订阅一个或多个指定模式,不指定参数则取消所有已订阅的模式
PING PING 订阅者阻塞状态下可执行,用于测试与 Redis 服务器的连接,服务器返回 PONG

注意:

阻塞态下的可操作边界

  • 支持的操作
    仅能执行「订阅 / 取消订阅」相关指令(SUBSCRIBEUNSUBSCRIBEPSUBSCRIBEPUNSUBSCRIBE),以及心跳测试指令 PING,这些指令执行后不会退出阻塞态。
  • 不支持的操作
    无法执行 GETSETHSET 等普通数据操作指令,这些指令在阻塞态下执行会报错。

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 是轻量级方案,不适合对消息可靠性、持久性有严格要求的场景,核心局限性如下:

  1. 无消息持久化机制:Redis 不会存储发布的消息,若订阅者离线(未在线接收),则会永久丢失该期间的消息,且消息发送后立即被丢弃,无法回溯历史消息。
  2. 无消息确认与重试机制:发布者发送消息后,Redis 仅负责转发,不保证订阅者是否成功接收、处理消息,无重试、重传机制。
  3. 无消息堆积能力:若订阅者处理消息的速度慢于发布者发送消息的速度,Redis 不会缓存未处理的消息,会直接丢弃超出处理能力的消息(无队列堆积)。
  4. 集群环境下支持有限:Redis 集群中,Pub/Sub 消息仅在当前节点转发,不会跨节点同步,若订阅者和发布者连接不同节点,可能无法接收消息。
  5. 不支持消息过滤与优先级:无法对消息进行精细化过滤,也不支持消息优先级排序,所有消息均按发送顺序转发。

五、与 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 的返回值可直观判断是否有订阅者接收消息。

七、总结

  1. Redis Pub/Sub 是轻量级广播通信机制,非独立数据结构,部署简单、低耦合,适合实时通知、日志收集等低可靠性要求场景。
  2. 核心指令需区分订阅者(阻塞态,SUBSCRIBE/PSUBSCRIBE)和发布者(非阻塞态,PUBLISH)。
  3. 其最大短板是无持久化、无确认机制,不适合金融交易、订单处理等对消息可靠性要求高的场景,此类场景建议使用专业消息队列(如 RabbitMQ、Kafka)。
相关推荐
论迹2 小时前
【RabbitMQ】-- 高级特性
数据库·redis·分布式·消息队列·rabbitmq
L1624762 小时前
Prometheus、Cadvisor和Grafana体系完整学习手册
学习·grafana·prometheus
DarkAthena2 小时前
【GaussDB】解析GaussDB热补丁机制
数据库·gaussdb
石像鬼₧魂石2 小时前
WPScan 实战完整笔记(含环境搭建 + 避坑指南 + 命令手册)
数据库·安全
望云山1902 小时前
ESP32—S3学习--入门五个基础实验
嵌入式硬件·学习
想唱rap2 小时前
MySQL内置函数
linux·运维·服务器·数据库·c++·mysql
CQ_YM2 小时前
SQLite3 数据库与网页html
c语言·数据库·sqlite·html
isNotNullX2 小时前
什么是云计算?一文讲清云计算的概念与作用
数据库·云计算·企业管理
renhongxia12 小时前
学习基于数字孪生的质量预测与控制
人工智能·深度学习·学习·语言模型·自然语言处理·制造