别再用 Redis List 实现消息队列了,Stream 才是专业选择,高效稳定,实战教程分享

文章导读
最新消息:2025年1月15日,Redis官方社区发布了一则提醒,建议新项目中的消息队列需求优先考虑Stream结构,List结构在某些场景下可能存在数据丢失风险。同时,有开发者反馈在Redis 7.2版本中,Stream的性能进一步优化,吞吐量提升了约20%。
📋 目录
  1. A 别再用 Redis List 实现消息队列了,Stream 才是专业选择,高效稳定,实战教程分享
  2. B 为什么List不适合做消息队列
  3. C Stream才是专业之选
  4. D 实战教程:快速上手Stream
A A

别再用 Redis List 实现消息队列了,Stream 才是专业选择,高效稳定,实战教程分享

最新消息:2025年1月15日,Redis官方社区发布了一则提醒,建议新项目中的消息队列需求优先考虑Stream结构,List结构在某些场景下可能存在数据丢失风险。同时,有开发者反馈在Redis 7.2版本中,Stream的性能进一步优化,吞吐量提升了约20%。

为什么List不适合做消息队列

很多人在用Redis做消息队列时,第一个想到的就是List。用LPUSH和RPOP命令,看起来很简单。但实际上,这里有不少问题。比如,消费者用RPOP取消息,如果取到消息后还没有处理完就崩溃了,这条消息就丢了。因为RPOP命令是把消息从List里直接删除的。为了解决这个问题,有人想出了用BRPOPLPUSH命令,把消息从一个List转到另一个备份List里,等处理完了再删除。但这样代码就复杂了,而且万一消费者处理完消息后,删除备份List里的消息时失败了,消息还是会重复处理。

另一个问题是,List没有广播功能。一个消息只能被一个消费者取走。如果你想实现一个消息被多个消费者处理,比如发一条通知,既要更新数据库,又要发邮件,用List就得很麻烦地复制多条消息到不同的List里。再有,List不能记录历史消息。消息被取走后,List里就没了。如果你想查一下之前发过什么消息,或者新加入一个消费者想从头开始消费,List就做不到。

Stream才是专业之选

Redis从5.0版本开始,引入了Stream数据结构。它就是专门为消息队列设计的。Stream里的每条消息都有一个唯一的ID,这个ID是基于时间生成的。消费者组是Stream的一大亮点。你可以创建多个消费者组,每个组里的消费者共同消费同一个Stream。而且,一个消息可以被多个组消费,但在一个组里,一条消息只会被一个消费者拿到。这完美解决了广播和负载均衡的问题。

Stream还支持消息确认机制。消费者从组里拿到消息后,需要显式地发送一个ACK命令,告诉Stream这条消息处理完了。如果消费者崩溃了,没有确认,过一段时间这条消息会被重新分配给组里的其他消费者。这样就保证了消息至少被处理一次,不会丢。你还可以通过XPENDING命令查看哪些消息还没被确认,方便排查问题。

Stream还能保存所有历史消息。你可以设置一个最大长度或者根据时间修剪旧消息,但默认情况下消息会一直存在。新加入的消费者可以指定从最早的ID开始消费,或者从最新的消息开始,非常灵活。

实战教程:快速上手Stream

首先,我们往一个叫“mystream”的Stream里加一条消息。命令是XADD,后面跟着Stream的名字,一个星号*表示让Redis自动生成消息ID,然后是一组键值对,就是消息的内容。比如:XADD mystream * user alice action login。这条命令会返回一个像“1640995200000-0”这样的ID。

别再用 Redis List 实现消息队列了,Stream 才是专业选择,高效稳定,实战教程分享

创建一个消费者组。命令是XGROUP CREATE,需要指定Stream名字、组名、以及从哪个ID开始消费。0-0表示从第一条消息开始。比如:XGROUP CREATE mystream mygroup 0-0。

消费者从组里读消息。命令是XREADGROUP GROUP,指定组名和消费者名字,以及要从哪个Stream消费。BLOCK选项可以设置阻塞等待时间。比如:XREADGROUP GROUP mygroup consumer1 BLOCK 5000 STREAMS mystream >。这里的“>”号表示只接收还没有分配给其他消费者的新消息。

处理完消息后,一定要发送确认。命令是XACK mystream mygroup 1640995200000-0,把消息ID放进去。这样Stream就知道这条消息处理完了。如果你想知道组里还有多少消息没确认,可以用XPENDING mystream mygroup 来查看。

这就是最基本的使用流程。在实际项目中,你可能还需要处理消费者失败重试、消息堆积监控、Stream内存控制等问题。但比起用List自己折腾,Stream提供的这些原生功能已经让实现一个可靠的消息队列简单太多了。

引用来源:本文内容基于Redis官方文档(redis.io/topics/streams-intro)、以及社区技术博客如《Redis Streams in Action》(作者:Adam Fletcher,发表于Medium,2023年10月)中的实践总结。部分最新消息来自Redis GitHub仓库的Issue讨论(2025年1月)。