redis pub/sub注定丢消息,因其纯内存广播、无队列无offset无历史;生产环境应改用stream,支持消费者组、ack确认、消息回溯与持久化。

Redis原生PUB/SUB不解决消息丢失——它压根就没打算解决。所有“断连丢消息”“没订阅就发丢了”“重启收不到历史消息”的问题,不是配置没调好,而是机制上就不支持。要真正保障可靠性,必须换用STREAM。
为什么PUB/SUB注定会丢消息
这不是Bug,是设计选择:PUB/SUB在Redis里就是纯内存广播,没有队列、不记offset、不存历史。只要订阅者没连上、连上了但卡住、或者网络抖动1秒,那期间所有PUBLISH都进黑洞。
- 服务端不维护“谁订了什么、上次收到哪条”,重连后
SUBSCRIBE只从当前时刻开始收 -
redis-cli SUBSCRIBE channel之后再断再连,之前发布的消息完全不可追溯 - 集群/哨兵切换时存在毫秒级“消息黑洞”,主从同步延迟会让部分消息只存在于已下线的主节点内存中
XADD + XREADGROUP 是最直接的替代方案
Redis 5.0+ 的STREAM类型天然支持多消费者组、消息确认(XACK)、未读消息回溯(XRANGE或XREAD指定ID),这才是生产环境该用的方式。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 发布消息用
r.xadd('mystream', {'event': 'order_created', 'id': '123'}),消息自动持久化到流中 - 消费者组创建一次即可:
r.xgroup_create('mystream', 'mygroup', '$', mkstream=True),$表示从最新开始消费 - 启动消费时用
r.xreadgroup('mygroup', 'consumer1', {'mystream': '>'}, count=10, block=0),>代表只取新消息;若想补历史,把>换成0或具体消息ID - 处理成功后必须显式
r.xack('mystream', 'mygroup', message_id),否则消息会一直留在待处理队列里
容易被忽略的三个实操细节
用STREAM不等于自动可靠,这几个点踩中一个,照样丢。
-
XGROUP CREATE必须带mkstream=True,否则流不存在时会报错,而不是自动创建 - 消费者重启后,如果没调
XREADGROUP带NOACK参数去捞积压,又没手动XRANGE扫一遍,就会漏掉断连期间的消息 -
STREAM本身不自动清理,得配合XTRIM mystream MAXLEN ~1000防内存膨胀;别依赖MAXLEN 0,它只在插入时触发裁剪,不保证实时
真正难的不是写对XADD和XREADGROUP,而是把“消息生命周期管理”意识带进业务逻辑里:谁负责创建组、谁负责ACK、谁兜底重试、积压怎么监控——这些才是STREAM落地时卡住最多人的地方。










