redis pub/sub 消息体超100kb会卡主线程,因单线程串行处理序列化、拷贝、遍历订阅者及发送;应控制在100kb内,采用“轻量通知+按需拉取”或迁移到stream。

Redis Pub/Sub 的消息体一旦超过 100KB,就极大概率引发主线程卡顿、延迟毛刺甚至连接断开——这不是配置问题,是单线程模型的硬限制。
为什么大Payload会直接卡住Redis主线程
Redis 处理 PUBLISH 命令时不做任何大小校验,所有逻辑(序列化、内存拷贝、遍历每个订阅者连接、网络发送)都在单个主线程里串行执行。一个 2MB 的 JSON 消息,可能让主线程停顿 200ms 以上,期间所有其他命令(GET、SET、甚至 PING)全部排队等待。
- 现象:监控里
redis-cli --stat显示pubsub_channels内存持续上涨,INFO memory中used_memory_dataset异常跳升 - 根本原因:不是“慢”,而是“独占”——只要它在跑,别的事一律暂停
- 特别注意:
proto-max-bulk-len默认 512MB,但别信这个数字;实际应控制在1024 * 100字节(100KB)以内
拆分消息不如提前拒绝,但真要拆必须跨层协同
单纯把大消息切片发到多个频道(比如 order:12345:part1、order:12345:part2)反而更危险:订阅端需维护状态、拼接顺序、超时重试,出错概率翻倍,且仍无法规避单条 PUBLISH 的阻塞风险。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 真正可行的“拆分”,是业务层放弃在 Pub/Sub 里传原始数据,改用「轻量通知 + 按需拉取」模式
- 发布端只发 ID 和简短元信息:
PUBLISH order:updated '{"id":"12345","ts":1716645720}' - 订阅端收到后,异步调用
GET order:12345或查 DB / 调 API 获取完整数据 - 若必须保序或防丢失,直接迁移到
Stream,用XADD+XREADGROUP,天然支持分片、ACK、重试
订阅端预检长度必须在反序列化之前
很多团队在 Python 里写 json.loads(msg["data"]) 后才检查长度,这已经晚了——大对象早已分配,GC 压力陡增,还可能触发 OOM。
- 正确姿势:禁用自动解码,拿到原始
bytes后立刻测长:if len(data) > 102400: logger.warning("drop oversized"); continue - Python redis-py 示例:
redis.Redis(decode_responses=False)+ 手动判断msg.get("data")长度 - Node.js ioredis:监听
messageBuffer事件而非message,避免 UTF-8 解码开销 - Go redigo:用
c.Receive()得到redis.Message,直接读msg.Data字节切片
最易被忽略的一点:PUBLISH 前的长度检查,必须在最终序列化之后做——JSON 序列化中文会转 Unicode,原始字符串 50KB 可能膨胀到 150KB。别省那几毫秒,宁可多一次 len() 调用。










