# 23. Redis 中的发布/订阅(Pub/Sub)是如何实现的?
# 标准答案
Redis 的发布/订阅(Pub/Sub)是一种消息通信机制,允许多个客户端订阅同一个频道(channel),发布者可以向频道发送消息,所有订阅该频道的客户端都会收到消息。其底层实现基于 全局字典结构 和 事件驱动模型,依靠 Reactor 线程 监听频道事件,并使用 非阻塞 IO(epoll/kqueue) 推送消息。
# 答案解析
Redis Pub/Sub 主要由 发布者(Publisher)、订阅者(Subscriber) 和 Redis 服务器 组成。当订阅者订阅某个频道后,Redis 服务器会维护一个订阅关系表,当有发布者向该频道发布消息时,Redis 服务器会遍历所有订阅该频道的客户端,并将消息推送给它们。
# 1️⃣ Pub/Sub 的数据结构
Redis 内部使用 字典(dict)+ 链表 维护 频道-订阅者关系:
pubsub_channels
(哈希表):记录 频道(channel) 对应的 订阅者集合。pubsub_patterns
(链表):记录 模式匹配(pattern) 订阅的客户端。
示例:
SUBSCRIBE news
→ Redis 记录news
频道的订阅者列表PUBLISH news "Hello"
→ Redis 查找news
频道的所有订阅者,推送"Hello"
消息
# 2️⃣ 订阅与发布的过程
# ✅ (1) 订阅(SUBSCRIBE)
当客户端执行 SUBSCRIBE channel1
,Redis 服务器会执行:
- 将该客户端 ID 添加到
pubsub_channels[channel1]
订阅者列表。 - 监听该频道的消息,当
channel1
有新消息时,立即推送给该客户端。
127.0.0.1:6379> SUBSCRIBE news
Reading messages... (press Ctrl-C to quit)
2
底层实现:
- Redis 维护一个
pubsub_channels
字典,key=频道
,value=订阅该频道的客户端列表
。- 订阅者被存储在
dict<channel, list<client>>
结构中。
# ✅ (2) 发布(PUBLISH)
当某个客户端 PUBLISH news "Breaking News!"
,Redis 会执行:
- 查找
pubsub_channels
,获取所有订阅news
的客户端。 - 使用 Redis 的事件驱动模型(Reactor)非阻塞推送消息。
- 如果订阅者很多,Redis 会遍历所有订阅者,逐个推送消息。
127.0.0.1:6379> PUBLISH news "Breaking News!"
(integer) 3 # 表示有 3 个客户端接收到了消息
2
底层实现:
- Redis 遍历
pubsub_channels["news"]
的所有订阅者,并向它们发送"Breaking News!"
。- 由于 Redis 单线程处理,它使用 高效的事件循环(epoll/kqueue) 进行消息分发。
# 3️⃣ Redis 的消息推送机制
Redis 采用 基于事件驱动的 IO 多路复用(epoll/kqueue),当 PUBLISH
触发时,Redis 会:
- 遍历所有订阅该频道的客户端。
- 将消息加入到客户端的输出缓冲区(output buffer)。
- 通过 Redis 的
write
事件发送数据给订阅者(非阻塞 IO)。
🚀 高效推送机制:
- 单线程模型:避免锁竞争,所有操作 O(1) 或 O(N)(N 为订阅者数)。
- 事件驱动:采用 IO 多路复用,消息发布后,Redis 立即推送给订阅者,避免轮询。
# 4️⃣ 模式匹配订阅(PSubscribe)
Redis 支持模式匹配订阅,允许订阅者监听 多个匹配的频道。
127.0.0.1:6379> PSUBSCRIBE news*
底层原理:
- Redis 维护
pubsub_patterns
,其中存储dict<pattern, list<client>>
。PUBLISH "news_sports" "Score: 3-1"
时,Redis 会匹配所有符合news*
模式的订阅者,并发送消息。
# 5️⃣ Pub/Sub 的缺陷
- 消息无持久化:如果订阅者掉线,离线消息不会存储,重连后无法获取之前的消息。
- 消息无确认机制:Redis 采用**“尽力而为”**的方式推送消息,无法保证所有订阅者都收到。
- 订阅端阻塞:当客户端
SUBSCRIBE
某个频道后,该连接进入阻塞模式,只能接收消息,无法执行其他命令。
# 深入追问
🔹 Redis Pub/Sub 和 Kafka、RabbitMQ 有什么区别?
🔹 如何在 Redis Pub/Sub 上实现持久化消息?
🔹 Redis 如何优化 Pub/Sub 的推送性能?
# 相关面试题
🔹 Redis 的 Pub/Sub 适用于什么场景?
🔹 Redis 如何处理大量订阅者的消息推送?
🔹 如何基于 Redis 实现可靠的消息队列?
# 总结
Redis Pub/Sub 是基于 事件驱动+字典索引 结构实现的消息发布/订阅模型,具有高吞吐、低延迟的特点,但缺少持久化和确认机制,适用于实时消息推送,但不适用于可靠消息队列。🚀