Redis发布订阅不是入门首选,因其设计为阻塞式轻量广播,不保证消息可达;SUBSCRIBE后连接仅响应四类控制命令,需专用连接,且无持久化、确认或重试机制。

Redis 发布订阅不是“入门首选模式”——它压根就不适合当分布式系统的学习入口,除非你明确只学轻量广播、且能接受它不保证消息可达。
为什么 SUBSCRIBE 一执行就卡住,根本不像普通 Redis 命令?
因为 SUBSCRIBE 会让客户端进入阻塞监听状态,不再响应其他命令(比如 GET、SET),只能处理 SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE/PUNSUBSCRIBE 这四类控制指令。这不是 bug,是设计使然:Redis 不维护订阅者连接状态,靠 TCP 长连接 + 阻塞读实现低开销广播。
- 一旦误在非专用连接上调用
SUBSCRIBE,该连接后续所有业务命令都会失败,报错类似ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context - Java 客户端(如 Jedis)必须用单独的
JedisPubSub实例,不能复用业务连接池里的连接 - Go 的
redis.Client默认不支持阻塞命令,得用redis.UniversalClient或显式调用Subscribe()方法并另起 goroutine 处理消息
为什么发了 PUBLISH 却没人收到?常见断连场景
Redis Pub/Sub 是纯内存、无持久化的实时通道,只要中间任意一环断开,消息就彻底丢失。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 订阅者进程崩溃或网络中断后重连,
SUBSCRIBE要重新执行——之前错过的所有消息全丢,Redis 不补发 - 发布者用
PUBLISH返回值判断“1 个订阅者收到”,但这个数字只统计当前在线连接数,不包含已断连但尚未超时踢出的连接(Redis 不主动探测连接存活) - 集群模式下:
SUBSCRIBE只作用于当前连接的节点,而PUBLISH会被路由到 key 所在 slot;若频道名没哈希到同一节点,消息直接静默丢弃——所以集群中必须禁用 Redis Cluster,改用单节点或哨兵模式
PSUBSCRIBE 模式订阅的 * 到底怎么匹配?
PSUBSCRIBE 的通配符 * 是 shell 风格,不是正则;它只匹配 0 个或多个任意字符,且不支持 ?、[abc] 等其他语法。
-
PSUBSCRIBE news.*→ 匹配news.sports、news.2024、news(注意:末尾无点,*可为空) -
PSUBSCRIBE *.alert→ 匹配db.alert、cache.alert,但不匹配alert(前面必须有字符) -
PSUBSCRIBE order_*→ 匹配order_paid、order_refund,但不匹配order:paid(:不是通配目标,只是字面量) - 一个客户端可同时
SUBSCRIBE和PSUBSCRIBE,但消息会重复收到:比如SUBSCRIBE order_paid且PSUBSCRIBE order_*,发PUBLISH order_paid "ok"会触发两次回调
真正该警惕的:它根本不是 MQ
别拿 PUBLISH/SUBSCRIBE 当 Kafka 或 RabbitMQ 用——它没有确认机制、无重试、无消费位点、不存消息体。一个典型翻车点是:用它做订单支付通知,结果库存服务刚启动就断网 2 秒,那秒内发布的 order_paid 消息永远消失,订单状态卡住。
- 需要可靠投递?选 Redis Stream(支持消费者组、ACK、pending list)或真正 MQ
- 只需要“尽力通知”?比如刷新缓存、踢用户下线、广播配置变更——这时
SUBSCRIBE才是够用又轻量的选择 - 所有客户端必须自己实现心跳保活、断线重连、消息去重(比如用
message_id字段 + 本地 Set 缓存最近 5 分钟 ID)
最常被忽略的一点:Redis Pub/Sub 的吞吐优势只在千级并发订阅者以内成立;一旦订阅者超 5000,单节点带宽和 CPU 会明显成为瓶颈,此时横向扩展无效——因为每个 PUBLISH 都要复制 N 份发给每个订阅连接,不是共享内存广播。

















