redis pub/sub 不保证消息可靠投递,无ack、不存盘、不缓存历史;大数据量下易因网络抖动、gc暂停等批量丢消息;应改用redis streams实现持久化、消费组与ack机制。

Redis Pub/Sub 本身不保证消息可靠投递
Pub/Sub 不是消息队列,它没有 ACK、没有重试、不存盘、不缓存历史。只要订阅者没连着,消息就彻底消失。大数据量下这个问题会被放大:连接抖动、网络分区、客户端 GC 暂停、服务端主从切换,都会导致批量丢消息。
常见错误现象包括:SUBSCRIBE 后收不到刚 PUBLISH 的消息、扩容新消费者收不到旧消息、服务重启后漏掉关键事件。
- Redis 服务器只维护当前在线订阅者的链表,
pubsub_channels字典里没有离线记录 - 消息发布时遍历链表推送,推送完即丢,不写 AOF/RDB,也不进任何队列
- 哪怕开了
appendonly yes,PUBLISH命令本身也不会被持久化(它不是影响数据状态的命令)
用 Redis Streams 替代 Pub/Sub 才能支撑大数据量
Streams 是 Redis 5.0+ 引入的持久化消息结构,支持消费组、ACK 确认、消息重传、历史回溯,这才是真正可落地的大数据量消息方案。
典型使用场景:事件溯源、订单状态变更广播、日志聚合分发——这些都需要“至少一次”或“精确一次”语义。
-
XADD写入消息,自动按时间戳排序并持久化到 AOF/RDB -
XGROUP CREATE创建消费组,多个消费者可协同处理同一批消息 -
XREADGROUP读取消息后需显式XACK,未 ACK 的消息保留在PENDING列表中,可重试 - 即使消费者宕机,其他成员也能通过
XPENDING+XCLAIM接管未完成任务
别指望哨兵或集群提升 Pub/Sub 可靠性
Redis Sentinel 和 Cluster 都不改变 Pub/Sub 的 fire-and-forget 本质。主节点故障时,正在发布的消息会中断;从节点不参与 Pub/Sub 转发;集群模式下跨 slot 的频道订阅甚至可能失败。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
错误认知示例:“我用了三主三从集群,Pub/Sub 就稳了”——实际是错的。集群只负责 key 分片路由,而 SUBSCRIBE 是全局命令,必须由所有节点响应,但 Redis Cluster 对 Pub/Sub 的支持是有限且易出问题的。
- 集群中
SUBSCRIBE默认只在本地节点注册,跨节点订阅需客户端自行路由(多数客户端库不自动处理) - 主从切换期间,SUBSCRIBE 连接会断开,
CLIENT LIST中对应连接消失,无自动重连机制 - 哨兵只管 failover,不管消息是否已送达,它不感知 Pub/Sub 的订阅状态
真要硬用 Pub/Sub,必须自己补可靠性层
如果受限于架构或成本,非用 Pub/Sub 不可,那只能在应用层兜底:加心跳检测、带序号的消息体、客户端本地缓冲 + 重拉机制、配合外部存储做消息快照。
但这会让简单逻辑变得复杂,而且容易踩坑:比如用 INCR 做序列号,在主从异步复制下可能重复;又比如重拉依赖时间窗口,而 Redis 不提供按时间范围查 Pub/Sub 历史的功能(它根本没有历史)。
- 不要依赖
PING/INFO判断连接可用性——SUBSCRIBE连接可能仍存活但收不到消息 - 避免在单个连接上混用
SUBSCRIBE和业务命令,阻塞会导致订阅失活 - 用
CLIENT SETNAME标记订阅连接,便于CLIENT LIST里快速识别和监控
最常被忽略的一点:Pub/Sub 的吞吐优势只在低延迟、低一致性要求场景成立;一旦加上重试、去重、持久化等逻辑,它反而比直接用 Kafka 或 RabbitMQ 更难维护。










