PSUBSCRIBE使用单向链表导致PUBLISH时O(N)遍历匹配,而SUBSCRIBE基于哈希表平均O(1);pattern匹配开销远高于字符串比较,且PUBLISH返回值统计强制遍历链表,未清理的旧pattern会持续拖累性能。

PSUBSCRIBE遍历的是链表,不是哈希表
Redis内部维护两套订阅结构:server.pubsub_channels(哈希表,用于SUBSCRIBE)和server.pubsub_patterns(单向链表,用于PSUBSCRIBE)。每次PUBLISH执行时,它必须遍历整个pubsub_patterns链表,对每个注册的pattern做shell风格匹配——哪怕只有一个PSUBSCRIBE news.*,也要走一遍O(N)全量扫描。
而SUBSCRIBE ch1是直接查哈希表,平均O(1);PUBSUB CHANNELS也只扫pubsub_channels,不碰pattern链表。
pattern匹配开销远高于字符串相等判断
shell通配符*匹配需要逐字符解析、回溯比较,比如news.*匹配news.sports.2024要拆成多段尝试。Redis没用正则引擎,但朴素实现仍比strcmp慢一个数量级。
- 匹配失败时,仍要完成整条pattern的扫描流程
- 多个
PSUBSCRIBE叠加(如PSUBSCRIBE *.alert order_*)会让遍历+匹配成本线性增长 -
PSUBSCRIBE注册本身不慢,但它的“负债”是在后续每一次PUBLISH里偿还的
Redis 7.0 的SSUBSCRIBE才是真解法
Redis 7.0 引入SSUBSCRIBE,底层用跳表替代链表,pattern索引从O(N)降到O(log N)。但注意:SSUBSCRIBE不兼容老客户端,且只支持前缀匹配(如news:),不支持news.*这种中间通配。
如果你还在用PSUBSCRIBE且频道数超1k,CPU热点基本就钉死在pubsubUnsubscribePattern和matchPattern这两个函数上。
容易被忽略的隐性放大点:PUBLISH返回值统计也扫pattern链表
PUBLISH命令的返回值是“收到消息的订阅者总数”,这个数字包含两部分:匹配SUBSCRIBE的 + 匹配PSUBSCRIBE的。也就是说,哪怕你只是想发个通知、完全不关心谁收到了,Redis仍会强制遍历pubsub_patterns来算这个数。
这导致两个后果:
- 即使没有pattern订阅者,只要链表非空,每次
PUBLISH都白跑一趟遍历 -
PUBSUB NUMPAT这类命令也会触发相同遍历,进一步拖慢主线程
真正压垮CPU的,往往不是消息体本身,而是你忘了清理早已不用的PSUBSCRIBE连接——它们留下的pattern节点,会持续参与每一次广播的O(N)扫描。











