Redis 6.0+ 的 PUBLISH 不阻塞主线程,因消息分发改由独立客户端上下文异步处理;而 5.x 及更早版本中 publish 同步遍历订阅者,网络慢或缓冲区满时会阻塞主线程。

Pub/Sub 的 PUBLISH 命令全程卡在主线程
Redis 所有 PUBLISH 操作都在单线程主线程中同步执行,不是“发完就走”,而是要为每个订阅者逐个做三件事:查匹配客户端列表、序列化消息、把字节拷贝进该 client 的 output buffer。哪怕只有 10 个订阅者,也得串行跑 10 次内存拷贝;涨到 1000 个,耗时基本线性翻 100 倍。这不是配置能调的,是 O(N) 硬伤。
Redis 6.0+ 的 io-threads 对它完全无效——I/O 多线程只加速 socket 读写,不碰消息遍历和缓冲区填充。实测开启 4 个 I/O 线程后,TPS 几乎无变化。
-
PUBSUB NUMSUB和PUBSUB CHANNELS同样跑主线程,channel 数超 10k 时可能单次阻塞 >5ms - pattern 订阅(如
news.*)比 channel 订阅更慢,因需遍历链表匹配,Redis 7.0 的SSUBSCRIBE改用跳表才缓解 - 别指望“加机器”提升单频道吞吐——Pub/Sub 是广播,不是队列;多起一个订阅进程,只是让 Redis 多做一次拷贝
client-output-buffer-limit pubsub 配置不当直接断连
默认 client-output-buffer-limit pubsub 继承自 normal 行(即 0 0 0),但实际生产中这个值必须显式设。否则一旦某个订阅者处理慢或网络抖动,它的输出缓冲区就会持续堆积,触发强制断连,且不会重试。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
推荐起点配置:client-output-buffer-limit pubsub 32mb 8mb 60 —— 硬上限 32MB,软上限 8MB 持续 60 秒即断。这个值需写进所有 Redis 实例(含哨兵、集群节点)的 redis.conf,热加载不生效,必须重启。
- 设成
0 0 0表示不限制,但单个慢订阅者可能吃光内存 - 软上限时间(第三个参数)别设太短,否则网络抖动就断;也别设太长,否则主线程被拖死
- 用
redis-cli info clients观察mem_clients_pubsub,突增说明缓冲区已在积压
消息体过大或高频小消息加剧主线程压力
PUBLISH 自身耗时通常
- 消息体建议控制在 1KB 内,超限优先考虑 Protocol Buffers 或 gzip 压缩
- 高频事件(如心跳、状态上报)尽量合并,用数组或 delta 编码减少频次
- 避免在
pubsub.listen()循环里做 HTTP 请求、DB 写入等耗时操作,否则缓冲区立刻暴涨
压测脚本误用 redis-benchmark 导致结论失真
redis-benchmark -t pubsub 只模拟单发布者 + 单订阅者复用同一连接,完全绕过真实瓶颈:多连接缓冲区竞争、文件描述符耗尽、pattern 匹配开销、断连重连雪崩。它跑出 80w+ ops/s,线上 50 个 Python 订阅线程就断连丢消息。
真实压测必须用多线程/多进程构造独立连接:
- 每个线程用独立
redis.Redis()实例和pubsub对象 - 订阅不同 channel(如
news:0~news:49),避免单 channel 竞争掩盖问题 - 用
CLIENT LIST查omem值,超过几 MB 就说明缓冲区已积压 - 观察
redis-cli info clients中client_longest_output_list,超 1000 就危险


















