根本原因是客户端读取消息不及时,导致Redis输出缓冲区堆积溢出而断连,继发重连雪崩;大消息加剧消费瓶颈,单条超1MB即属设计越界,应改用Stream或对象存储。

根本原因不是Redis发不出去,而是客户端读不及时,导致连接缓冲区被撑爆、连接被强制断开,进而触发重连雪崩。
消息体积大会直接触发 client-output-buffer-limit pubsub 保护机制
Redis 对每个 Pub/Sub 连接都维护一个输出缓冲区(output buffer),用来暂存待发送给客户端的消息。这个缓冲区大小受 client-output-buffer-limit pubsub 控制,默认是 8mb 2mb 60:硬限 8MB、软限 2MB 持续 60 秒就断连。
- 一条 1MB 的消息进来,如果客户端消费逻辑阻塞(比如 Python 里用
pubsub.listen()却在回调里做同步 HTTP 调用),缓冲区立刻开始堆积 - 10 条未处理消息就能轻松突破 2MB 软限,60 秒后连接被 Redis 主动踢掉
- 客户端重连后重新
SUBSCRIBE,若积压未清空,新连接又重复进缓冲区——形成“断连→重连→再断连”循环
大消息加剧客户端单线程/单 goroutine 处理瓶颈
Pub/Sub 消费本质是串行的:哪怕你订阅了 100 个 channel,消息仍按到达顺序一条条进入客户端事件循环。大消息会显著拉长单次处理耗时。
- Python 的
redis-py默认listen()是同步阻塞调用,处理一条 5MB JSON 解析可能耗时 200ms,期间无法响应新消息 - Go 的
github.com/go-redis/redis/v9虽用 goroutine 分发,但如果回调函数里做json.Unmarshal或写磁盘,依然会卡住该 goroutine - Node.js 的
ioredis基于net.Socket,但大消息触发 V8 内存拷贝和 GC,也会拖慢message事件分发频率
网络与内核层面对大消息更不友好
单条消息过大,会放大 TCP 层和系统调用的开销,尤其在高并发订阅场景下。
- Redis 发送大消息需多次
write()系统调用,而客户端read()若未及时触发,socket receive buffer 快速填满 - Linux
net.core.rmem_max默认通常为 212992 字节(约 208KB),远小于典型大消息(如日志切片、序列化模型参数),导致频繁 packet loss 或 retransmit - 不要指望调大
rmem_max解决问题——它只是把“缓冲区溢出”从 Redis 侧转移到内核 socket buffer,最终仍体现为丢包或超时
真正关键的不是“怎么让 Redis 多发”,而是“怎么让客户端快读、快解析、快释放”。消息体超过 100KB 就该警惕,超过 1MB 基本意味着设计已越界——Pub/Sub 不是传文件协议,该换 Stream 或落地到对象存储了。











