Redis Pub/Sub 不支持死信队列,因其不持久化消息、无ACK机制、无法追踪投递状态;可靠场景应改用Redis Streams,通过消费者组、pending list和显式XADD到DLQ流实现等效功能。

Redis Pub/Sub 本身不支持死信队列(DLQ),这不是遗漏,是设计剔除——它压根不存消息,自然谈不上“失败后转存”。补救方向只有一个:别用 Pub/Sub 做需要可靠投递的场景。
Pub/Sub 为什么连死信队列的影子都没有
死信队列的前提是:消息可持久、可标记状态、可重路由。而 PUBSUB 的实现里:SUBSCRIBE 连接一断,服务器立刻丢弃所有待发消息;PUBLISH 返回成功 ≠ 消息被消费,只是“已广播给当前在线连接”;没有 offset、没有 ACK、没有重试上下文。你甚至没法判断某条消息是否被谁收到过。
常见错误现象包括:
- 消费者处理异常后未主动断连,但 socket 缓冲区满导致连接静默中断,后续消息全丢
- 用
PSUBSCRIBE order.*监听通配符,网络抖动恢复后需重新PSUBSCRIBE,但断连期间的order.created、order.paid全部不可追溯 - Spring Boot 中配置了
lettuce: reconnect-delay,重连后仍收不到断连期消息——因为 Redis 根本没存
必须换用 Redis Streams 才能模拟 DLQ 行为
Streams 是唯一在 Redis 内原生支持“失败后重试 + 显式归档”的数据结构。它不提供叫 dead-letter-stream 的配置项,但你可以用消费者组 + pending list + 显式转移实现等效逻辑。
实操建议:
- 生产者改用
XADD orders * event_type created order_id ABC123,消息自动持久化 - 消费者启动时执行
XREADGROUP GROUP mygroup worker1 COUNT 10 STREAMS orders >,从最新消息开始拉取 - 处理失败时,不要直接跳过,而是用
XADD dlq * source_stream orders failed_id 1712345678901-0 error "timeout"主动写入自定义死信流 - 另起一个补偿任务定期扫描
XPENDING orders mygroup,对超时未XACK的 ID 主动迁移或告警
千万别在 Pub/Sub 上“打补丁”模拟 DLQ
有人试图在应用层加缓存兜底:发布前先写 SET pubsub_backup:1712345678901 "msg" EX 300,订阅失败再查这个 key。这不仅增加延迟和复杂度,还引入新问题:
- 备份 key 和实际
PUBLISH不是原子操作,可能只写 key 成功但PUBLISH失败,造成假“已备援” - 多个消费者同时处理同一条消息时,无法协调谁该去读 backup key
- backup key 过期策略难定:设太短,来不及人工干预;设太长,内存泄漏
更麻烦的是,这种方案完全绕开了 Redis 的语义一致性,等于自己造了个半吊子队列,却还要承担 Pub/Sub 的不可靠性。
真正容易被忽略的点是:很多团队在压测时发现 Pub/Sub “看起来不丢”,是因为测试环境网络稳定、服务不重启、消费者永远在线——但生产环境的滚动发布、K8s pod 驱逐、Lettuce 连接池回收,都会触发那 100–500ms 的断连窗口,而这个窗口期的消息,永远无法被任何客户端感知到存在过。


















