PUB/SUB不支持削峰填谷,因无缓冲、不落盘、不排队;真正实现削峰填谷需用LIST或STREAM等有状态、可持久、可回溯结构。

PUB/SUB 本身不支持削峰填谷——它没有缓冲、不落盘、不排队,消息一发即逝。想靠它扛住突发流量,等于让快递员把包裹直接扔进人群里,没人接就丢。
真正能做削峰填谷的,是 LIST、STREAM 这类有状态、可持久、可回溯的数据结构。但 PUB/SUB 可以配合它们用,比如用它广播“有新订单来了”,下游消费者再从 STREAM 拉取具体数据。
SUBSCRIBE 阻塞连接在高并发下会快速耗尽连接数
- Redis 的
SUBSCRIBE是长连接,每个订阅者独占一个连接;一旦订阅多个频道或使用PSUBSCRIBE,连接不会复用 - 默认
maxclients是 10000,但实际中 2000+ 活跃订阅者就可能触发ERR max number of clients reached - Java 客户端(如 Lettuce)默认为每个
MessageListener启动独立连接,没做连接复用时极易打满
建议:
- 用单个连接 + 多频道订阅(
SUBSCRIBE ch1 ch2 ch3),而不是为每个频道建新连接 - 避免在循环里反复调用
SUBSCRIBE,订阅一次即可,重复执行会报ERR already subscribed to this channel - 若必须模式匹配,优先用
PSUBSCRIBE alert.*而非大量SUBSCRIBE alert.error、alert.warn等分散频道
PUBLISH 在突发流量下会阻塞主线程,且不保证送达
-
PUBLISH是同步命令:Redis 主线程遍历所有订阅者连接,逐个写入 socket;若某订阅者网络卡顿或处理慢,整个PUBLISH调用会被拖住 - 没有重试、无 ACK、无消费确认;客户端断连期间的消息永久丢失
- 单次
PUBLISH向 5000 个订阅者发消息,实测延迟可能飙到 200ms+,直接拖垮上游服务
应对方式:
- 绝对不要在核心交易链路(如下单、支付回调)中直接
PUBLISH - 把发布行为异步化:先写入
STREAM或LIST,再由后台 worker 异步PUBLISH通知(仅作轻量广播) - 对关键消息,改用
XADD+XREADGROUP,它支持 ACK、pending list、多消费者负载分摊
用 STREAM 替代 PUB/SUB 实现真削峰填谷
-
STREAM是追加写、有序、可分组、带游标、支持消费者组的持久化结构,天然适合削峰 - 示例:订单创建后写入
XADD orders * order_id 123 status created,下游多个 worker 通过XREADGROUP GROUP wg1 c1 COUNT 10 STREAMS orders >拉取并 ACK
关键配置注意点:
- 创建消费者组时必须指定
START位置,否则新组无法读历史消息:XGROUP CREATE orders wg1 $ MKSTREAM -
XPENDING要定期巡检,防止消息卡在 pending list 中超时未处理 - 不要给
STREAM设MAXLEN过小(如MAXLEN ~ 1000),否则削峰能力归零;按峰值 5 分钟积压量预估长度
PUB/SUB 的价值不在扛流量,而在低延迟广播控制信号——比如“清空本地缓存”“切换灰度开关”“触发配置热重载”。它快、轻、无状态,但也因此极难监控和兜底。真正要填谷,得靠 STREAM 的 backlog 和消费者组的伸缩性;而削峰,本质是把“推”变成“拉”,把“即时”变成“可重试”。

















