当前位置:首页 > 问答 > 正文

Redis消息队列那些常用指令,简单聊聊怎么用和注意点

说到Redis消息队列,最核心、最常用的就是List(列表)这种数据结构,你可以把它想象成一个两头都能进出的管道,基于这个管道,就产生了两种最简单的消息队列模式。

第一种,也是最简单的,叫LPUSH/RPOP模式。(来源:Redis官方文档对List命令的介绍)

  • 怎么用:消息的生产者(比如一个后台任务)使用 LPUSH 命令,把消息从管道的左边(头部)一个一个放进去,就像排队买票,后来的人站在队伍的左边末尾,消费者(比如处理任务的程序)则在管道的右边(尾部)用 RPOP 命令,把消息一个一个取出来处理,保证先来的消息先被处理(FIFO,先进先出)。
  • 注意点:这里有个大问题!如果消费者使用 RPOP,它是一条“非阻塞”命令,意思是,当消费者去取消息时,如果管道是空的,它拿到一个 nil(空值)后就立刻返回了,然后程序继续往下执行,那消费者怎么知道下一秒钟有没有新消息来呢?它只能傻傻地不停地用循环去问:“有消息吗?有消息吗?”,这种方式叫做“轮询”,非常浪费CPU资源,效率很低。

为了解决这个“傻等”的问题,就有了第二种更常用的模式,使用BRPOP命令。(来源:Redis官方文档对阻塞命令的介绍)

  • 怎么用:这个模式生产者不变,还是用 LPUSH,但消费者变了,它使用 BRPOP 命令,这个“B”代表“Blocking”(阻塞),消费者执行 BRPOP myqueue 0 后,就会卡在那里静静地等待,这里的“0”代表超时时间,0就是无限等待,直到有消息进来,一旦生产者放入一条消息,消费者就会立刻被“唤醒”,拿到消息并进行处理,处理完后,它再次进入阻塞等待状态,这种方式非常高效,CPU不会被白白消耗。
  • 注意点
    1. 消息丢失风险:虽然 BRPOP 解决了效率问题,但还有一个隐患,当消费者从队列里取出消息后,这条消息就在Redis里被删除了,如果消费者在处理消息的过程中突然崩溃了,还没来得及处理完,那这条消息就永远丢失了,这对于要求可靠性的业务(比如扣款、下单)是无法接受的。

为了解决消息丢失的问题,Redis提供了更高级的“可靠队列”模式,这就要用到Redis的另一个数据结构——Stream(流)。(来源:Redis官方关于Stream数据结构的文档)

  • 怎么用:Stream就像是只能追加的日志文件,生产者用 XADD 命令向一个Stream(流)中添加消息,每条消息都会有一个自动生成的唯一ID,消费者这边呢,它不再是简单地“取出并删除”消息,而是使用 XREAD 命令来“读取”消息,关键在于,每个消费者(或消费者组)都需要记录一个“最后处理的消息ID”(ACK),消费者读取一条消息后,开始处理,处理成功后,必须显式地发送一个 XACK 命令,告诉Redis:“这条消息我处理完了,你可以把它标记为已完成”,如果消费者崩溃了,当它重启后,可以根据自己记录的最后ID,去读取那些“已发送但未确认(ACK)”的消息,重新处理,这样就保证了消息至少被处理一次。
  • 注意点
    1. 复杂度更高:Stream的功能强大,但命令也比List复杂不少,需要理解消费者组(Consumer Group)、待处理条目(PEL)等概念。
    2. 资源消耗:因为消息不会被立刻删除(直到被明确确认或超过保留时间),Stream会比List消耗更多的内存,需要设置合理的消息留存策略。
    3. 重复消费:“至少一次”的语义可能导致同一条消息被处理多次(比如消费者ACK确认时网络失败),所以你的业务逻辑需要能够容忍重复处理,或者自己实现幂等性(即同一个操作执行多次,结果和执行一次一样)。

除了上述核心模式,还有一些常用的辅助指令和技巧:

  • 发布/订阅(Pub/Sub):(来源:Redis官方文档对Pub/Sub的介绍)这严格来说不是队列,而是一种广播机制,生产者 PUBLISH 一个消息到某个频道,所有订阅(SUBSCRIBE)了这个频道的消费者都会同时收到这条消息,它的问题是没有持久化,如果消费者当时不在线,就永远收不到这条消息了,通常用于在线聊天、实时状态广播等场景。
  • 查看队列长度:用 LLEN key 可以查看一个List队列里有多少条消息在等待处理,用于监控。
  • 一次处理多条消息:使用 LPOPRPOP 时加上数量参数,LPOP myqueue 5,可以一次性取出多条消息,在某些场景下能减少网络交互,提升效率。

总结一下选择和注意点:

  • 简单、可丢失的任务:比如更新内存缓存、发送一条不重要的统计日志,用 LPUSH + BRPOP 就够了,简单粗暴。
  • 重要、不可丢失的任务:比如订单处理、支付通知,必须使用 Stream 来保证可靠性。
  • 广播类场景:比如通知所有在线用户系统要维护了,可以用 Pub/Sub,但要接受消息可能丢失。
  • 通用注意点
    • 监控:一定要监控队列的长度,如果队列堆积越来越长,说明消费者处理不过来了,是系统瓶颈的预警信号。
    • 序列化:存入Redis的消息通常是字符串,你需要把对象(比如一个JSON)序列化成字符串存进去,取出来再反序列化,要约定好格式,比如都用JSON。
    • 键名设计:给队列起个清晰易懂的键名,queue:order:pay(订单支付队列),方便管理。

Redis消息队列的魅力就在于它的轻量和灵活,用几个简单的命令就能搭建起不同特性的消息通路,但你需要根据业务的严肃性,在简单性和可靠性之间做出权衡。

Redis消息队列那些常用指令,简单聊聊怎么用和注意点