PUB/SUB 无持久化,消息随连接断开即丢失,不可用于可靠消息场景;需改用 Redis Stream + Consumer Group 并显式 XACK。

PUB/SUB 本身没有持久化机制,任何试图“在无持久化前提下提升其数据安全性”的做法,都是在对抗设计本质——它注定不可靠。真要保消息不丢,必须换结构,不是调参数。
为什么所有“增强 PUB/SUB 安全性”的补丁都失败了
你见过的这些方案,基本都踩过坑:
- 用数据库存一份消息再
PUBLISH:业务逻辑变重,双写不一致风险高,且没解决“订阅者断连后怎么拉取”问题 - 加 Redis
LIST+SET模拟队列:手动维护消费者状态、重复投递、位点错乱、OOM 风险(LIST 不设MAXLEN) - 依赖客户端自动重连 + 重订阅:重连后仍从当前时间开始收,
PSUBSCRIBE通配符也无法回溯 - 把
PUBLISH改成事务包裹:MULTI/EXEC对PUBLISH无效,命令压根不进事务队列
根本原因就一条:PUB/SUB 的消息生命周期 = TCP 连接存活期。连接断,消息即焚。
哪些场景其实可以继续用 PUB/SUB?
不是所有通知都需要可靠。如果你的业务满足以下任意一条,PUB/SUB 仍是合理选择:
- 消息内容是瞬时状态,比如 WebSocket 在线用户数变更,下一秒就会被新值覆盖
- 允许丢失,且有兜底轮询或定时补偿(如配置热更新,5 秒内没收到就主动
GET一次) - 仅用于开发/测试环境广播调试日志,生产环境另走通道
- 服务发现心跳广播,只要最近一次在线即可,历史心跳无意义
一旦涉及“订单创建”“支付成功”“风控拦截”这类事件,PUB/SUB 就该立刻退出。
必须切换到 Redis Stream 的三个硬性信号
当出现以下任一现象,说明你已经在用 PUB/SUB 承担它不该承担的责任:
- 监控告警里频繁出现 “consumer offline for >2s” 且对应业务有资损
- 上线后发现
XINFO CONSUMERS mystream mygroup返回空,但XPENDING里堆积上千条未确认消息 - 用
XREADGROUP拉不到消息,查XINFO STREAM mystream发现last-generated-id已远超消费位点,但PEL为空——说明压根没建组或建组时用了0而非$
这时候别修,直接切。最小改动路径是:把所有 PUBLISH channel msg 替成 XADD mystream * event msg,再补上 XGROUP CREATE mystream mygroup $ MKSTREAM 和 XREADGROUP GROUP mygroup c1 COUNT 10 STREAMS mystream >。
Stream 实操最容易漏掉的三件事
很多人切了 Stream 还丢消息,问题不在结构,而在细节:
- 忘记给
XADD加MAXLEN ~ N:不设就无限追加,AOF 文件暴涨,重启加载慢甚至失败 -
XGROUP CREATE时写成0而非$:新消费者一启动就拉几万条历史消息,下游直接 OOM - 消费者名写成随机字符串(如
"c_" + uuid4()):等于不断注册新 consumer,旧消息永远没人XACK,XPENDING持续增长
真正安全的边界不在 Redis 配置里,而在你是否敢让业务逻辑依赖“某条消息一定被处理”。如果答案是肯定的,那就没有商量余地:删掉所有 SUBSCRIBE,全部迁移到 Stream + Consumer Group + 显式 XACK。











