psubscribe 比 subscribe 多消耗 cpu 的根本原因是每次 publish 都需对全部 pattern 线性 glob 匹配,无索引、不短路、不缓存;而 subscribe 依赖哈希表实现 o(1) 查找。

PSUBSCRIBE 比 SUBSCRIBE 多消耗 CPU,根本原因不是“功能更高级”,而是每次 PUBLISH 都要对全部 pattern 做一次线性 glob 匹配——没有索引、不短路、不缓存。
PSUBSCRIBE 的 glob 匹配是纯 CPU 密集型操作
Redis 服务端把所有 PSUBSCRIBE 注册的 pattern 存在链表里(server.pubsub_patterns),每次 PUBLISH channel message 时,主线程必须遍历整个链表,对每个 pattern 调用 glob 函数比对 channel 字符串。
- 匹配逻辑类似 shell:* 匹配 0 或多个字符,? 匹配单个字符,但 * 不匹配空字符串(
order.*不会命中order) - 大小写敏感:
Order.*≠order.*,但 CPU 仍会完整跑完这次无效匹配 - 无短路机制:哪怕第一个 pattern 就匹配成功,后续所有 pattern 仍会被逐个检查
- 无缓存:相同
channel反复发布,每次都是全新字符串扫描
pattern 数量和长度直接决定 CPU 开销
匹配耗时与 pattern 总数呈线性关系,且单次匹配成本随 pattern 长度上升。常见高负载场景:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
PUBSUB NUMPAT返回值 > 100 时,单次PUBLISH延迟常从 0.1ms 升至 2ms+,几乎全是 glob 循环开销 - pattern 写成
service.order.v2.us-east-1.*.failed这类长串,比order.*多出数倍比对步骤 - 混用冗余符号如
order.*.?*,不仅无法提升覆盖,还强制多一层字符判别 - 每秒
PUBLISH1000 次 + 80 个 pattern = 每秒 8 万次 glob 调用
SUBSCRIBE 是 O(1) 查表,PSUBSCRIBE 是 O(N) 遍历
SUBSCRIBE channel 依赖哈希表 server.pubsub_channels,查 channel 是直接 hash 定位;而 PSUBSCRIBE pattern 的匹配路径完全绕过哈希,只能链表遍历。
-
PUBSUB NUMSUB channel是 O(1),但PUBSUB NUMSUB ch1 ch2 ch3参数越多,遍历开销越线性增长 -
PUBSUB CHANNELS *在 channel 数超 10k 时可能卡主线程 >5ms,因为要扫完整张哈希表 - Redis 6.0+ 的 I/O 多线程对
PSUBSCRIBE完全无效——瓶颈在内存遍历,不在网络层 - Redis 7.0 的
SSUBSCRIBE才真正改用跳表索引,实现 O(log N) 匹配
客户端误用 pattern 是最隐蔽的 CPU 杀手
你没法提前验证 pattern 是否有效,错误写法照常吃 CPU 却不发消息:
-
PSUBSCRIBE user:*对user:id:123无效(冒号 : ≠ 点 .),但每次PUBLISH仍白跑一遍 glob - 用时间戳或 UUID 当 channel 名(如
log.20260903.123456),导致 pattern 完全无法复用,频道爆炸 - 没设
client-output-buffer-limit pubsub,缓冲区积压触发主线程反复刷写,CPU 持续高位 - 客户端未监听
error或reconnect事件,连接断开后反复PSUBSCRIBE,形成隐式高频重订阅
真正关键的不是“少用 PSUBSCRIBE”,而是明确知道:只要 pattern 数超过 20 个,或 QPS 超过 100,你就已经站在 CPU 毛刺的临界点上——这时候改写法比调参管用得多。










