Redis PUB/SUB是无确认、无重试、无持久化的广播模型,消息仅内存暂存,断连即丢失;PSUBSCRIBE支持glob模式匹配但不支持正则和空格转义,且订阅连接不可执行常规命令,不可替代可靠消息队列。

Redis 的 PUB/SUB 本身是广播模型,但不保证消息可达
Redis 的 PUBLISH / SUBSCRIBE 天然就是广播:向一个频道 PUBLISH 一条消息,所有当前已订阅该频道的客户端都会收到。它不区分“单播”或“多播”,只有“有订阅者就发,没订阅者就丢”。没有确认、无重试、无持久化——PSUBSCRIBE 同样如此,只是匹配逻辑从精确字符串变成 glob 模式匹配。
常见误解是以为“广播 = 可靠”,其实恰恰相反:如果客户端网络中断、进程崩溃、或刚启动还没来得及 PSUBSCRIBE,那条消息就永远消失了。这不是 bug,是设计使然。
PSUBSCRIBE 能动态匹配频道,但模式不能包含空格或特殊字符
PSUBSCRIBE 接收的是 glob 风格通配符,支持 *(任意字符序列)、?(单个字符)、[abc](字符类),但不支持正则、不支持转义空格、不支持嵌套括号。例如:
PSUBSCRIBE logs:* # ✅ 匹配 logs:err、logs:info:2024 PSUBSCRIBE user:123:* # ✅ PSUBSCRIBE user:*:order # ✅ PSUBSCRIBE "user:*" # ❌ 引号会被当作字面量,实际订阅的是带引号的频道名 PSUBSCRIBE user:123 order:456 # ❌ 多个模式必须用多个 PSUBSCRIBE 命令,不能空格分隔
实操建议:
- 模式中避免使用
:以外的分隔符(如.或/),因为部分客户端库对点号转义处理不一致 - 上线前用
redis-cli手动测试模式是否如预期匹配:PSUBSCRIBE test:*→ 另起窗口PUBLISH test:abc hello - 不要依赖模式“自动发现”未声明的频道;服务端不会推送频道列表,客户端必须预先知道模式规则
多个 PSUBSCRIBE 客户端共享同一连接会互相干扰
Redis 不允许在同一个 TCP 连接上混用 SUBSCRIBE 和 PSUBSCRIBE,也不允许对同一模式重复 PSUBSCRIBE(虽不报错,但无意义)。更关键的是:一旦连接进入订阅模式,它就只能处理发布/订阅命令,不能再执行 GET、SET 等常规命令——否则 Redis 直接返回错误 ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context。
这意味着:
- 不能在一个连接里先
PSUBSCRIBE,再想顺手INCR个计数器 - Web 应用若用常驻连接做
PSUBSCRIBE,必须单独开一个连接,和业务连接隔离 - 某些语言客户端(如 Python 的 redis-py)默认把
pubsub()返回对象绑定到新连接,但若手动复用连接对象,容易踩这个坑
消息丢失风险比想象中高,别把它当消息队列用
很多人用 PSUBSCRIBE 实现“微服务间通知”,比如订单创建后广播给库存、风控等服务。但只要有一个下游服务重启延迟 > 1 秒,或者网络抖动丢包,那条消息就彻底消失。Redis 官方文档明确说:PUB/SUB 是“fire-and-forget”,不是消息队列。
如果你需要可靠传递:
- 用
STREAM+XREADGROUP,支持消费者组、ACK、pending list、消息重传 - 或引入真正 MQ(Kafka/RabbitMQ),哪怕只是本地部署的轻量版
- 若坚持用 PUB/SUB,至少加一层补偿:关键事件落地 DB 后再
PUBLISH,下游监听失败时主动查 DB 补偿
最常被忽略的一点:Redis 主从切换期间,从节点不接受 PUBLISH,而客户端若配置了读写分离,可能误连从节点发消息——结果悄无声息地丢了。务必确保 PUBLISH 总打到主节点。











