NUMSUB仅统计当前活跃连接中的订阅者,即主线程执行时处于SUBSCRIBE/PSUBSCRIBE监听态的客户端;断连、超时或未进入listen循环均导致计数为0,且受集群、代理、ACL及动态频道名影响,本质是调试命令而非监控指标。

NUMSUB 只统计当前活跃连接里的订阅者
它不是“历史累计”或“逻辑上该频道该有多少人”,而是 Redis 主线程在执行命令那一刻,扫描所有客户端连接,只把那些正处于 SUBSCRIBE 或 PSUBSCRIBE 监听态(即已调用 listen() 并阻塞等待消息)的连接算进去。一旦连接断开、脚本退出、超时关闭,计数立刻归零。
常见误判场景包括:
- 用
redis-cli手动执行SUBSCRIBE mychan后直接关掉终端:这个连接根本没来得及被其他连接看到,PUBSUB NUMSUB mychan必然返回 0 - Python 中调了
pubsub().subscribe("mychan")却没进listen()循环,或者循环里没做实际读取——Redis 不认为它是“活跃订阅者” - Java 客户端用 Jedis 的
subscribe()方法但没启动监听线程,或线程异常退出后未重连
代理、集群和 ACL 会让 NUMSUB 返回 0 或报错
Redis Cluster 对 Pub/Sub 支持不完整:SUBSCRIBE 默认只注册到本地节点,而 PUBSUB NUMSUB 若发到非订阅节点,就查不到任何数据;Twemproxy、Codis 等代理中间件通常直接拦截或忽略 PUBSUB 命令,导致始终返回空数组或 (error) ERR unknown command。
ACL 权限不足也会静默失败:
- 用户没有
+@pubsub权限,PUBSUB NUMSUB会返回空结果,而不是报错 - ACL 规则中没配
~*,即使有 pubsub 权限,也可能因频道名不匹配被过滤
频道名动态生成导致 NUMSUB 查不到真实订阅关系
如果业务用 user:12345:1734681960 这类带时间戳或 UUID 的字符串做频道名,每次都是新频道,PUBSUB NUMSUB user:12345:1734681960 当然返回 0——因为没人长期订阅这个瞬时频道,且旧连接早已断开。
这时更值得看的是:
-
PUBSUB CHANNELS user:12345:*能否列出一批相似前缀的频道 -
INFO clients中的connected_clients和client_longest_output_list是否异常高——说明有大量慢订阅者堆积消息,但频道本身已失效 -
CLIENT LIST里找omem大的连接,再用CLIENT GETNAME确认它是否还在监听某个已无人问津的频道
NUMSUB 不是监控指标,不能用于判断业务是否正常
它本质是个调试命令,不是可观测性指标。生产环境频繁调用 PUBSUB NUMSUB 本身就会加重主线程负担,尤其当频道数过万时,O(N) 匹配开销明显。
真正需要关注的信号是:
-
INFO memory中的mem_clients_pubsub占比是否持续 >25% -
CLIENT LIST输出中是否存在omem> 10MB 且idle> 300 秒的连接 -
PUBSUB NUMPAT值是否突增——正则订阅比普通订阅匹配成本高一个数量级
频道爆炸和订阅者失联往往同步发生,但 NUMSUB 返回 0 只是表象,背后可能是内存泄漏、客户端未正确 UNSUBSCRIBE、或频道命名策略失控——这些点比数字本身重要得多。


















