Redis Pub/Sub 消息丢失是设计使然:它不持久化、无状态、无确认,仅在发布瞬间向在线客户端广播并立即释放,离线、断连或未订阅时消息直接丢弃且不可恢复。

会直接丢弃,且无法恢复——这不是故障,是 Redis Pub/Sub 的设计本质。
Pub/Sub 下线即丢消息的底层原因
Redis Pub/Sub 不维护任何消费状态:没有消息存储、不记录偏移量、不保存订阅者进度。它只是在 PUBLISH 执行瞬间,把消息拷贝一份发给所有当前 SUBSCRIBE 在线的客户端连接,然后立即释放。
-
PUBLISH返回值为 0,代表“此刻无人在线”,消息当场蒸发 - 哪怕订阅者 100ms 前刚连上又断开,这期间发布的消息也完全不可见
-
PSUBSCRIBE通配符订阅、redis-cli重连后重新SUBSCRIBE,都拿不到历史消息
为什么不能靠 LIST 或事务“补救”
有人尝试用 LPUSH/LRANGE 模拟队列,或在 SUBSCRIBE 前用 pipeline 读取伪队列——这些方案在并发或重连场景下极易出错:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 多个订阅者同时上线时,
LRANGE+DEL非原子,消息可能被重复消费或漏消费 -
PUBLISH和LPUSH不在一个事务里,网络分区时会出现“消息进了队列但没广播”或反之 - LIST 无自动过期,长期积压会撑爆内存;也没 ID 序号,无法精准定位断连起始点
真正可用的离线兜底方案只有 Stream
必须放弃用 Pub/Sub 承担可靠性职责,改用 XADD + XREAD 实现持久化事件流:
- 每条消息写入前先
XADD stream:u1001 * event "xxx",确保离线也能落盘 - 用户上线后查本地缓存的 last_id(比如存在
user:lastread:u1001),再XREAD STREAMS stream:u1001 last_id+1 - 务必注意顺序:先
XADD,再PUBLISH;否则网络抖动可能导致消息只广播未落库
最容易被忽略的是:Pub/Sub 和 Stream 不是二选一,而是分层协作——PUBLISH 负责秒级触达在线用户,XADD 负责兜住所有离线时段。混用时若顺序颠倒或缺少幂等校验,就会出现“用户收到了消息却没存下来”或“存了两次”的数据不一致。

















