Redis Pub/Sub 消息持久化问题详解

Redis Pub/Sub 消息持久化问题详解

Pub/Sub 的核心特点:即发即忘

Redis Pub/Sub(发布/订阅)是一种消息通信模式,它的核心设计理念是 “即发即忘”(fire and forget)

发布者 → Redis Channel → 订阅者(接收时在线)
                         → 订阅者(离线 → 消息丢失)

关键特性:
– 消息 不持久化,发送后即被丢弃
– 离线订阅者 无法收到 离线期间的消息
– Redis 不存储消息,只是转发

Pub/Sub 消息模型

基本操作

# 订阅者
SUBSCRIBE news:sports       # 订阅频道
SUBSCRIBE news:*            # 不支持通配符,需用 PSUBSCRIBE
PSUBSCRIBE news:*           # 模式订阅

# 发布者
PUBLISH news:sports "球赛结果:3-1"  # 发布消息

消息流转过程

PUBLISH news:sports "消息内容"
    ↓
Redis 查找该频道的所有订阅者
    ↓
向每个订阅者推送消息
    ↓
消息推送完成后立即丢弃
    (无论是否送达成功)

Pub/Sub 的可靠性问题

1. 消息不持久化

# 订阅者连接断开
# 此时发布者发送 10 条消息
PUBLISH channel "msg1"
PUBLISH channel "msg2"
# ... 10 条消息全部丢失

# 订阅者重连
SUBSCRIBE channel  # 只能收到重连后的消息

2. 没有消息确认机制

  • 发布者不知道消息是否被成功接收
  • Redis 不关心订阅者是否真的收到了消息
  • 网络抖动会导致消息丢失而无人知晓

3. 缓冲区溢出

# client-output-buffer-limit 配置
client-output-buffer-limit pubsub 32mb 8mb 60
  • 每个订阅者维护一个输出缓冲区
  • 如果订阅者消费慢,缓冲区满后 Redis 会断开连接
  • 断开期间的消息全部丢失

4. 模式订阅的额外风险

PSUBSCRIBE news:*      # 可能匹配大量频道
# 高频发布导致缓冲区快速填满
# 连接被强制断开

为什么 Pub/Sub 不做持久化

Redis 的设计哲学

  1. 定位不同:Pub/Sub 是即时消息通道,不是消息队列
  2. 性能优先:不做持久化,转发速度极快(微秒级)
  3. 内存管理:存储消息会消耗大量内存,违背 Redis 作为缓存的定位

与其他 Redis 消息模式的对比

特性 Pub/Sub Stream List (BLPOP)
消息持久化 ✅ RDB/AOF ✅ RDB/AOF
离线消息 ❌ 丢失 ✅ 可回溯 ✅ 堆积
消息确认 ✅ XACK ✅ 弹出即确认
广播支持 ✅ 多消费者组
消费者组

适用场景

Pub/Sub 适合的场景

  1. 实时通知:如 WebSocket 广播、实时排行榜更新
  2. 轻量级事件:缓存失效通知、配置变更广播
  3. 即时聊天:内存聊天室(不需要消息历史)
  4. 监控告警:实时指标推送

不适合的场景

  1. 订单处理:消息不能丢失,需要持久化
  2. 任务调度:需要可靠投递和重试
  3. 日志收集:数据需要持久化和回溯
  4. 重要通知:订阅者离线时必须保留

如果需要持久化怎么办?

方案一:改用 Redis Stream

# 用 Stream 实现持久化 Pub/Sub
# 生产者
redis.xadd('notification:channel', {'event': 'user_login', 'uid': '123'})

# 消费者(每个消费者独立消费者组)
redis.xreadgroup('notification:group', 'consumer1', 
                 {'notification:channel': '>'})

方案二:Pub/Sub + Stream 组合

# Pub/Sub 做实时推送
# Stream 做持久化备份

# 接收消息的进程
def on_message(channel, data):
    # 1. 实时推送给当前在线订阅者
    send_to_online_subscribers(channel, data)
    # 2. 同时持久化到 Stream
    redis.xadd(f'persist:{channel}', data)

# 离线订阅者重连时,从 Stream 拉取离线消息
def on_reconnect(user_id):
    pending = redis.xreadgroup(f'group:{user_id}', 'consumer', 
                               {f'persist:{channel}': '0'})

面试要点

  • Pub/Sub 核心缺陷:消息不持久,离线即丢失
  • 缓冲区溢出:消费慢+生产快=连接断开
  • 无 ACK 机制:无法保证消息送达
  • 适用场景:实时性 > 可靠性,消息价值短暂
  • 面试高频题:对比 Pub/Sub vs List vs Stream 的消息模型
  • 如果问”如何让 Pub/Sub 可靠”,正确的回答是改用 Stream
© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容