redis pub/sub断连必丢消息是设计使然,因其纯内存广播、无队列、无offset、不持久化,断连期间消息彻底消失,重连后也无法获取历史消息。

Redis Pub/Sub 断连就丢消息,是设计决定的,不是配置能改的
它压根不存消息,也没有“待投递队列”这个概念。只要客户端 SUBSCRIBE 连接断开(哪怕只断 1 秒),期间所有 PUBLISH 的消息就彻底消失——不会进 AOF,不会写 RDB,Redis 甚至不记录“这条消息该发给谁”。你调 CONFIG SET appendonly yes 或改 save 规则,对 Pub/Sub 零影响。
CLIENT LIST 里 omem 突增,其实是缓冲区快爆了才被强制断开
很多人以为“断连=网络问题”,其实更常见的是输出缓冲区超限触发的主动杀连接。Redis 为每个订阅连接单独维护一个内存缓冲区,默认硬限才 8MB。一旦消费者处理慢(比如 GC 停顿、日志刷盘卡住),消息就堆在缓冲区里,涨到 client-output-buffer-limit pubsub 设定值,Redis 就直接 close 连接,缓冲区内容清空。
- 查证方式:
CLIENT LIST中找cmd=subscribe的 client,看omem字段是否飙升 - 典型误判:看到 “disconnected” 日志就去查网络,却没盯
INFO memory里的used_memory_human是否异常上涨 - 调参要点:必须同时设三个值,例如
client-output-buffer-limit pubsub 32mb 8mb 60;软限soft-seconds要大于客户端心跳间隔,否则会误杀
重连后 SUBSCRIBE 收不到历史消息,因为 Redis 根本没记过
SUBSCRIBE 不是“注册一个长期订阅关系”,而是一次性开启一个实时管道。断连重启后执行 SUBSCRIBE channel,Redis 只从命令执行那一刻起转发新消息,之前发布的全无痕迹。这不是 offset 没同步,是压根没有 offset 这个东西。
- 对比
Stream:XREADGROUP GROUP mygroup c1 STREAMS mystream >中的>表示“未分配过的消息”,靠的是服务端维护的消费者组状态和消息 ID 序列 - Pub/Sub 没有频道级状态持久化:
redis-cli里先PUBLISH ch "hi",再开新窗口SUBSCRIBE ch,照样收不到 - 集群切换时更危险:主节点上刚
PUBLISH完,还没来得及同步到从节点,主就故障了——这条消息永远消失
别在业务关键链路里用原生 Pub/Sub,除非你真能接受丢
订单创建、支付回调、库存扣减这类场景,只要有一条消息丢了,后续就可能产生资损或状态不一致。Pub/Sub 的“即发即弃”模型在这里不是轻量,而是不可控。真正难的不是换技术,而是把“消息必须被看见、被确认、被清理”这个闭环意识带进代码里——比如用 XACK 后还要监控 XPENDING 数量,用 XTRIM 控制流长度,而不是只写对 XADD 和 XREADGROUP 就算完事。











