pub/sub适合心跳、状态广播、纯实时信令等场景,因其零延迟、无状态、发布即焚特性,不保证消息持久化与投递,专为发完即走、收不到就算的轻量广播设计。

Pub/Sub 适合什么场景:心跳、状态广播、纯实时信令
Pub/Sub 不是“弱化版消息队列”,它压根就不是为业务事件设计的。它的核心价值在于零延迟、无状态、发布即焚——比如 WebSocket 服务向 500 个在线连接广播 "user:123:online",或集群节点每秒发一次 ping 到 cluster:heartbeat 频道。这种流量不要求重试、不关心谁收到了、也不需要回溯。
常见错误现象:SUBSCRIBE 后客户端断开,重连再 SUBSCRIBE 就收不到断连期间的消息;用 redis-cli --csv SUBSCRIBE ch1 测试时 Ctrl+C 退出后收不到历史消息。这不是 bug,是设计使然。
- 订阅者数量上涨时,
PUBLISH的内存拷贝压力线性增长,Redis 内存和网络带宽可能先扛不住 -
PSUBSCRIBE user:*支持动态模式匹配,但XGROUP CREATE要提前声明 group 名,无法按连接 ID 灵活分组 - 没有
ACK,也没有PEL(Pending Entries List),自然不支持故障转移或重放
Stream 适合什么场景:订单、积分、埋点等需可靠投递的业务事件
Stream 是 Redis 5.0 引入的持久化日志结构,本质是一本带编号的流水账。XADD 写入即落盘(AOF/RDB),XREADGROUP 拉取后必须显式 XACK,否则消息会留在 PEL 中等待重试。它真正对标的是轻量级 Kafka,不是 Pub/Sub 的替代品。
典型误用:PUBLISH order:created {"id":"ord_abc"} 用于支付回调后的库存扣减——一旦库存服务重启 2 秒,这条消息就永远丢失。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 单个 Stream 可被多个消费者组独立读取,
group-a和group-b的游标互不影响 - 消息 ID 默认是
1718234567890-0这类时间戳+序号,人工指定必须单调递增,否则XADD报错ERR The ID specified in XADD is equal or smaller than the target stream top item -
XTRIM配合MAXLEN控制保留天数,不 trim 会导致内存持续增长
List 队列为什么不能直接替代 Stream
用 LPUSH/BRPOP 做队列,看似简单,但本质是“抢答模式”:消息一旦被某个消费者 BRPOP 出来,就从链表中永久删除。如果该消费者处理失败又没做幂等或补偿,消息就丢了。
更关键的是,它不支持“一对多”消费。比如支付成功要同时触发积分、通知、风控三个动作,List 只能靠一个消费者拉取后手动 fan-out,把可靠性押在单点逻辑上。
-
BRPOP queue 0的阻塞超时设为 0 是安全的,但若误写成BRPOP queue 1(1 秒超时),空轮询会抬高 CPU - 没有内置偏移量管理,无法实现“从某条消息重新开始消费”,审计或问题排查基本靠猜
- 无法天然支持消费者组语义,横向扩展只能靠分片(如
queue:shard:0~queue:shard:3),运维复杂度陡增
选型时最容易忽略的三个硬约束
很多团队卡在“技术参数差不多”的幻觉里,实际落地时栽在细节上:
- Pub/Sub 的
PUBLISH返回值是订阅者数量,不是“发送成功”。如果返回(integer) 0,说明当前没人在线订阅——这在心跳场景是正常行为,在业务事件里却是严重告警信号 - Stream 的
XREADGROUP必须先用XGROUP CREATE初始化 group,且 group 名不可重复;而 Pub/Sub 的SUBSCRIBE是即用即建,无初始化成本 - Redis 实例若启用了 AOF 重写或 RDB bgsave,Stream 的写入吞吐会阶段性下降,但 Pub/Sub 完全不受影响——这对实时信令服务反而是优势










