Appearance
Redis Stream 和 List 队列怎么选
很多团队第一次用 Redis 做消息队列,往往是从 List + LPUSH/BRPOP 开始。
后面看到 Stream 后,又很容易觉得:
“那 List 不是可以退休了?”
其实不是。
这两个结构并不是简单的“新旧替代关系”,而是适合不同复杂度的队列需求。
先说结论
- 只需要简单先进先出、单消费者阻塞取消息,
List通常更轻更直接 - 需要消费组、未确认消息追踪、重试和回放能力时,优先考虑
Stream - 两者都不适合替代专业 MQ 去承接高可靠、跨系统、大规模异步链路
- 选型重点不是功能多少,而是你是否真的需要“消费状态管理”
先看 List 队列在干什么
用 List 做队列,本质就是:
- 生产者
LPUSH - 消费者
RPOP或BRPOP
它的优势非常直接:
- 简单
- 命令少
- 性能直观
- 适合单消费链路
典型适用场景:
- 应用内部轻量异步任务
- 日志削峰
- 短生命周期临时队列
List 队列的核心短板
List 最大的问题不是不能用,而是“消息被取走后,Redis 不再帮你记住它的消费状态”。
这会带来几个后果:
- 消费者拿到消息后挂了,消息可能丢
- 想做失败重试,要自己补处理中间状态
- 多消费者协作时,只能靠应用层额外兜底
当然你也可以做一套 BRPOPLPUSH + processing list 之类的方案,
但这其实已经在自己手搓“半个消息中间件”了。
Stream 为什么会更像消息系统
Stream 除了保存消息本身,还引入了几个关键能力:
- 消息 ID
- 消费组
- pending list
- ack 确认
这意味着 Redis 开始帮你记录:
- 哪条消息被谁消费了
- 哪些消息还没确认
- 哪些消息可能需要别人接管
这也是它比 List 更适合“需要恢复和重试”的根本原因。
一个最直观的区别
List
像把纸条从盒子里拿走。
拿走以后,盒子不知道谁拿了,也不知道纸条后来有没有处理成功。
Stream
像一本可追踪的收件登记簿。
你能看到消息是谁领走的、是否确认、哪些还没处理完。
什么时候 List 更合适
下面这些场景,List 反而比 Stream 更合适:
- 只有一个消费逻辑
- 丢一条消息影响不大
- 处理失败可以容忍少量补偿
- 你更看重实现简单
比如:
- 站内埋点短暂削峰
- 非核心异步通知
- 本机任务分发
这类场景用 Stream 往往有点重。
什么时候该上 Stream
如果你开始关心下面这些问题,就应该认真考虑 Stream:
- 消费者挂了,消息怎么办
- 多个消费者如何协作
- 失败消息怎么重试
- 某条消息处理慢,能不能被别人接管
- 能不能看到 pending 堆积
也就是说,当你开始关心“消费过程是否可见”时,Stream 的价值就出现了。
Stream 也不是白送的
很多人看到 Stream 有消费组,就误以为它可以直接替代 Kafka / RabbitMQ。
这也不准确。
你要知道它的代价:
- 结构更复杂
- 需要维护消费组和 ack
- pending 长时间不清会堆积治理成本
- 大规模消息链路和跨系统可靠投递仍不如专业 MQ 合适
Stream 更像“Redis 里最像消息队列的结构”,不是“专业 MQ 平替”。
一个实际判断模型
可以这样选:
选 List
如果你只要:
- 简单队列
- 单路消费
- 逻辑轻
- 失败影响可控
选 Stream
如果你需要:
- 多消费者组
- ack / pending 管理
- 失败恢复
- 明确的消费状态追踪
选 Kafka / RabbitMQ / RocketMQ
如果你需要:
- 高可靠异步解耦
- 大规模吞吐
- 跨系统长期积压
- 更成熟的消息治理能力
两个常见误区
1. 用 List 强行补很多消费状态
如果你已经开始自己维护:
- 待处理队列
- 处理中队列
- 重试队列
- 超时扫描
那大概率该评估 Stream 或专业 MQ 了。
2. 用 Stream 承载特别核心的大规模业务总线
Stream 能做很多事,但不是所有异步链路都应该塞进 Redis。
特别是高可靠、长堆积、跨团队消费链路,专业 MQ 会更合适。
总结
List 和 Stream 的真正区别,不在于谁更新,而在于:
- List 更像轻量任务队列
- Stream 更像带消费状态管理的队列
所以选型时最该问的不是“哪个功能更强”,而是“我到底需不需要让 Redis 帮我管理消费过程”。