一、为什么 Redis 一定要出 Stream?
发布订阅(Pub/Sub)的问题
优点
实时
简单
缺陷
不持久化
客户端离线 → 消息直接丢
无法回溯历史消息
本质原因:
Pub/Sub 是"广播事件",不是"存储消息"
List 实现消息队列的问题
优点
FIFO
可阻塞
问题一:消息一旦消费就没了
无法重复消费
消费失败,消息直接丢
问题二:没有 ACK 机制
消费者 RPOP 后宕机
消息已经被删除
问题三:ID 需要自己维护
分布式环境下很麻烦
本质原因:
List 是"容器",不是"消息日志"
二、Redis Stream 的设计目标
Redis 官方目标很明确:
做一个"真正的消息队列 / 消息日志系统"
Stream 要解决的问题:
| 能力 | 是否支持 |
|---|---|
| 消息持久化 | ✅ |
| 全局唯一 ID | ✅ |
| 消息不丢 | ✅ |
| 消费确认(ACK) | ✅ |
| 多消费者 | ✅ |
| 消费组 | ✅ |
| 消息回溯 | ✅ |
三、Stream 是什么?
Redis Stream 是一个"只追加的消息日志(Append-Only Log)"
Stream 的数据模型
消息 ID 是什么?
<毫秒时间戳>-<序列号>
特点:
全局有序
天然递增
分布式安全
Redis 自动生成(*)
为什么 ID 如此重要?
用来 定位消息
用来 断点续消费
用来 回溯历史消息
Stream 如何解决旧方案的问题?
1. 消息持久化
Stream 数据:
存在内存
写 AOF / RDB
Redis 重启 消息仍在
2.支持历史消息读取
可以从头读到尾
离线重连也能补消息
3. 自动生成全局唯一 ID
Redis 保证:
不重复
单调递增
4. ACK 确认机制
只有 ACK 后:
消息才算"已处理"
消费者宕机?
未 ACK 的消息会留在 Pending List
5. 消费组
这是 Stream 最重要的能力。
没有消费组(广播)
每个消费者都能读到所有消息
有消费组(负载均衡)
特点:
一条消息只会被一个消费者处理
天然负载均衡
非常适合后台任务、订单处理
Stream vs List vs Pub/Sub
| 特性 | Pub/Sub | List | Stream |
|---|---|---|---|
| 持久化 | ❌ | ✅ | ✅ |
| 消息确认 | ❌ | ❌ | ✅ |
| 重复消费 | ❌ | ❌ | ✅ |
| 消费组 | ❌ | ❌ | ✅ |
| 消息回溯 | ❌ | ❌ | ✅ |
| 适合生产 | ❌ | 勉强 | ✅ |