redis pub/sub 丢消息是设计使然,不是 bug:它采用纯内存广播模型,无持久化、无offset、无队列、不记录消费进度,订阅者断连或未订阅时发布的消息永久丢失,且发布成功不保证被接收。

Pub/Sub 丢消息是设计使然,不是 bug
Redis PUBSUB 从不承诺“至少一次”,它只做一件事:把当前在线的订阅者全部广播一遍。连接断开、客户端重启、Redis 服务重启——所有中间发布的消息,一律不补、不存、不记。
常见错误现象:PSUBSCRIBE 后收不到历史消息;网络抖动后重连,发现漏了一整段事件;用 PUBLISH 发完立刻返回 OK,但业务日志里压根没看到消费记录。
- 没有 offset,无法回溯消费位置
- 没有 unack 状态,出错后无法重投
-
SUBSCRIBE建立连接前发的消息,永远不可见 - Redis 重启后,
PUBSUB CHANNELS返回空,不是没频道,是整个状态清零
RabbitMQ 的 queue 是有“记性”的,Pub/Sub 没有
RabbitMQ 把消息写进磁盘(需显式设置 durable=true),消费者 basic.consume 后消息进入 unack 状态,崩溃或断连会自动重回队列;而 Redis PUBLISH 成功只代表写入 socket 缓冲区,不代表任何客户端真的收到了。
使用场景差异明显:
- 订单状态变更、支付回调、审计日志 → 必须用 RabbitMQ(或 Kafka/Stream)
- 服务健康心跳(如
service:api:ping)、缓存失效通知(丢一次也无妨)→PUBSUB足够 - 需要按 group 分流、重试、死信、TTL 延时 → RabbitMQ 原生支持,Redis 得靠
Stream+ 自研逻辑
别拿 List 或 Stream 当 Pub/Sub 用,它们解决的问题不同
很多人混淆 Redis 的三种“队列”能力:List 是 FIFO 队列,Stream 支持消费者组和 ACK,而 PUBSUB 是纯广播。误用会导致语义错乱。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
例如:
- 用
LPUSH/RPOP实现任务分发,但没做幂等或失败重入 → 任务可能重复执行或丢失 - 用
Stream却不调用XACK→ 消息不会从 pending 列表移除,下次拉取还会出现 - 用
PUBSUB推送用户事件,却期望每个消费者只处理一次 → 实际上所有订阅者都收到同一份副本
Stream 最接近 RabbitMQ,但它仍缺原生的死信队列、消息路由、集群高可用保障,运维成本不低。
上线前三个月最容易翻车的地方
真正麻烦的不是选型当天,而是三个月后某个凌晨,监控告警突然沉默——你才发现,那个用 PUBSUB 推送的“库存扣减完成”事件,在某次网络分区中全丢了,下游服务一直卡在旧状态。
容易被忽略的关键点:
- Pub/Sub 不提供消费指标,无法知道某频道是否还有活跃订阅者
- RabbitMQ 的
queue_declare可设auto_delete=false,Redis 没有类似机制 - 哪怕只差一个 ACK 机制,就决定了能不能做金融级状态流转










