百万qps下redis pub/sub的瓶颈在于cpu与内存耦合恶化:cpu高致分发延迟,延迟推高缓冲区,缓冲区满触发断连重连,形成恶性循环;需合理配置client-output-buffer-limit、控制pattern数量并规范客户端重连逻辑。

百万 QPS 的 Redis Pub/Sub 不是单纯“加机器就能扛住”的问题,它会在内存和 CPU 两个维度同时触发隐性瓶颈,且彼此耦合:CPU 高了导致消息分发延迟,延迟又推高输出缓冲区,缓冲区涨满再触发连接断开重连,形成恶性循环。真实压测中,90% 的崩溃点不在 PUBLISH 本身,而在 client-output-buffer-limit 未设、PSUBSCRIBE pattern 过多、或客户端监听逻辑失控。
client-output-buffer-limit 必须手动配置,否则内存会失控
Redis 对 pubsub 客户端的输出缓冲区默认有值(如 8mb 2mb 60),但这个“默认”不写进 redis.conf 就等于没设——CONFIG SET 不支持动态修改该参数。一旦某个订阅者消费卡顿,缓冲区就会按「单条消息体积 × 订阅数 × 卡顿时长」线性堆积。
- 例如:平均消息 120KB,8 个 tab 同时订阅,允许卡住 5 秒 → 理论积压 4.8MB,
soft-limit至少设为6mb,hard-limit设为16mb(留毛刺余量) -
CLIENT LIST中omem持续 >1MB 就该预警;>5MB 很可能已拖慢主线程,甚至触发OOM kill - 禁用限制(
0 0 0)等同于放弃内存保护,尤其在百万 QPS 下,几秒积压就能吃光数十 GB 内存
PSUBSCRIBE pattern 数量直接决定 CPU 开销
每次 PUBLISH 都要遍历所有已注册的 pattern 做 glob 匹配,无索引、无缓存、无短路。pattern 越多,CPU 耗时越接近线性增长——这不是客户端问题,是 Redis 服务端纯 CPU 密集型操作。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
PUBSUB NUMPAT返回值超 20,单次PUBLISH延迟就可能从 0.1ms 升至 1ms+;到 100+,CPU 毛刺明显,instantaneous_ops_per_sec反而可能下降 - pattern 写法影响匹配效率:
order.*.*比order.*多一次字符串扫描;Order.*(大写 O)永远不匹配order.xxx(大小写敏感),但 CPU 仍白跑一遍 - 没有调试命令验证 pattern 是否生效,只能靠发布后观察;线上误配 pattern + 百万 QPS = 白耗 CPU 还收不到消息
真实压测必须绕过 redis-benchmark,用多连接多频道模拟
redis-benchmark -t pubsub 只建一个发布者 + 一个订阅者,完全忽略广播模型的本质——百万 QPS 场景下,真正吃资源的是“1 发 N 收”的网络复制、缓冲区管理、连接调度,而非单条命令执行。
- 必须用 Python/Go 等构造 N 个独立 TCP 连接,每个连接订阅不同 channel(避免单 channel 竞争),并持续调用
pubsub.listen() - 每个线程必须用新
redis.Redis()实例,不能复用连接——否则 socket 共享会导致状态错乱、消息丢失 - 监听循环里禁止做耗时操作(如 HTTP 请求、DB 查询),否则消息积压 → 缓冲区暴涨 → 连接被踢 → 客户端无限重连 → CPU 100%
最容易被忽略的是:百万 QPS 下,问题往往不出在 Redis 自身,而出在客户端是否设置了 socket_timeout、是否监听了 reconnect 事件、是否用了指数退避重连。一个没设 timeout 的 get_message() 调用,在连接中断后可能退化成忙等,把应用进程 CPU 打满——而 Redis 的 INFO stats 里却看不出任何异常。










