redis pub/sub高cpu主因是缓冲区积压、频道爆炸和消费延迟,需调优client-output-buffer-limit、合并频道并监控pubsub指标。

Redis 发布订阅(Pub/Sub)本身不参与持久化、不走命令队列、不经过慢查询日志,但一旦消费者积压或频道数量失控,client-output-buffer-limit pubsub 缓冲区就会持续膨胀,导致主线程反复刷写、CPU 暴涨——这不是“功能太强”,而是配置和使用方式没对齐。
为什么 Pub/Sub 会吃光 CPU?看这三类典型现象
不是所有高 CPU 都是命令慢,Pub/Sub 的问题藏在缓冲区和事件循环里:
-
redis-cli info clients显示client_longest_output_list持续 > 1000,说明某个订阅客户端的输出缓冲区堆积严重 -
redis-cli info memory中mem_clients_normal不高,但mem_clients_pubsub占比突增(比如超 30%),直指发布端快、消费端慢 -
redis-cli monitor能看到大量PUBLISH命令瞬间涌入,但没有对应SUBSCRIBE或UNSUBSCRIBE日志,说明频道被滥建(如用时间戳/UUID 当频道名)
调整 client-output-buffer-limit pubsub 是第一道防线
默认值 client-output-buffer-limit pubsub 32mb 8mb 60 表示:缓冲区超 32MB 强制断连;60 秒内累计超 8MB 也断连。生产环境往往不够用,也不够严格:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 若消费者稳定但偶尔延迟,可收紧时间窗口:改
60为10,避免缓冲区“温水煮青蛙”式增长 - 若消息体大(如 JSON > 1KB),需同步调高硬限制:比如
128mb,但必须搭配监控告警,否则只是延缓崩溃 - 绝对不要设为
0(禁用限制)——这是把缓冲区失控的风险全交给操作系统处理,极易触发 OOM Killer 杀 Redis 进程
启用 IO 线程对 Pub/Sub 几乎无效,别白配
io-threads 和 io-threads-do-reads 只加速网络读取和响应组装,而 Pub/Sub 的瓶颈从来不在网络层,而在:主线程要把每条消息复制到每个订阅者的输出缓冲区。这个动作是纯内存拷贝 + 链表遍历,无法并行化。
- 开启
io-threads后,redis-cli info cpu里的used_cpu_sys可能略降,但used_cpu_user不变甚至上升(线程调度开销) - 真正有效的做法是减少“需要复制的次数”:合并频道(如用
news:tech替代news:tech:20260404)、强制消费者心跳保活、用PUBSUB NUMSUB定期清理零订阅频道 - 如果业务允许,直接换方案:用 Kafka / Pulsar 承担广播流量,Redis 只做状态缓存
频道数爆炸时,PUBSUB CHANNELS 和 PUBSUB NUMPAT 必须进巡检脚本
Redis 内部用跳表维护频道名,频道越多,每次 PUBLISH 前的匹配开销越大。当 PUBSUB CHANNELS * 返回结果超过 500 个,就要警惕:
- 用
PUBSUB NUMPAT查模式订阅数,> 10 就算高风险——正则匹配比字符串精确匹配慢一个数量级 - 禁止前端直接传任意字符串作频道名;后端统一做归一化(如哈希后截取 8 位)再订阅
- 定期执行
PUBSUB CHANNELS pattern*+UNSUBSCRIBE清理陈旧频道,别依赖客户端主动退订(移动端掉线很常见)
Pub/Sub 的 CPU 问题本质是“流控失位”:发布者不控速、消费者不保活、运维不巡检。IO 线程解决不了根本矛盾,真正要盯死的是缓冲区水位、频道基数和消费延迟这三个数字。










