百万用户聊天室不可用Redis PUB/SUB,因其单连接高内存占用、无分片、O(N)遍历、通配符匹配失控及不支持消息持久与重播。

不合适,百万在线用户用原生 Redis PUB/SUB 做聊天室会直接压垮连接和内存,不是调参能解决的工程问题。
为什么 SUBSCRIBE 会撑不住百万连接
每个订阅客户端在 Redis 中都维持一个独立的 TCP 连接 + 输出缓冲区。Redis 默认不合并消息、不复用连接,SUBSCRIBE 后客户端进入阻塞状态,服务端必须为每个连接单独维护订阅关系和待发送队列。实测表明:单节点 Redis 在 10 万级订阅者时,输出缓冲区(client-output-buffer-limit pubsub)就频繁触发强制断连;到 50 万+,CPU 和网络栈已严重争抢,延迟毛刺明显,PUBLISH 返回值开始失真(比如返回 1 但实际只推给部分客户端)。
- Redis 的
pubsub订阅表是纯内存字典 + 链表结构,无分片、无索引优化,O(N) 遍历所有订阅者 - 消息不落盘、不排队,慢消费者(如弱网移动端)一旦缓冲区满,连接被踢,且无法重播
- 集群模式下,
SUBSCRIBE只作用于当前节点,跨 slot 的频道订阅完全失效——你根本不知道用户该连哪台机器
PSUBSCRIBE 模式匹配在高并发下更危险
PSUBSCRIBE chat:* 看似方便,但背后是线性扫描所有 pattern 订阅者。百万级订阅者中若有几千个用了通配符,每次 PUBLISH chat:123 都要遍历全部 pattern 列表做 glob 匹配,CPU 使用率瞬间拉满。这不是“性能差”,而是算法复杂度失控。
- pattern 匹配无缓存、无前缀树,纯字符串逐个比对
-
PUBSUB NUMPAT返回的是 pattern 总数,不是活跃匹配数,监控失真 - 一旦有恶意 pattern(如
chat:*+chat:**),可能触发重复投递或 OOM
替代方案不是“换命令”,而是换架构
真要支撑百万在线聊天室,得绕开 PUB/SUB 的根本限制:
- 用
Redis Streams替代:支持消费者组、ACK、消息持久化、历史回溯,XREADGROUP可水平扩展多个 worker 处理同一频道 - 引入网关层分流:把用户按
room_id % N分散到不同 Redis 实例,再用SHARDSUBSCRIBE(Redis 7.0+)做分片广播 - 关键路径剥离:高频心跳、状态变更走
PUB/SUB,消息正文走Streams或 Kafka,避免大 payload 堵塞输出缓冲区 - 客户端必须实现断线重连 + 游标续读,不能依赖 Redis 自动补发——它根本不会补
最常被忽略的一点:PUB/SUB 的“实时”是建立在“全量广播+无状态”的前提下的,而百万在线聊天室天然需要消息去重、顺序控制、已读未读标记、离线存储——这些它一个都不提供,硬往上堆只会让问题在凌晨三点爆发。











