zset实现延迟队列

文章目录

      • [一、 核心设计思想:什么是 ZSet 延迟队列?](#一、 核心设计思想:什么是 ZSet 延迟队列?)
      • [二、 运行流程:存 与 取](#二、 运行流程:存 与 取)
        • [1. 生产者:存入任务(投递消息)](#1. 生产者:存入任务(投递消息))
        • [2. 消费者:取出任务(轮询消费)](#2. 消费者:取出任务(轮询消费))
      • [三、 深度理解:`ZRANGEBYSCORE` 在这里起什么作用?](#三、 深度理解:ZRANGEBYSCORE 在这里起什么作用?)
      • [四、 生产环境必须注意的"隐患"](#四、 生产环境必须注意的“隐患”)

将利用 Redis 的 Sorted Set(ZSet) 实现延迟队列的核心逻辑与 ZRANGEBYSCORE 命令的作用合并,我们可以用" "和""两个动作来完整还原这个方案。


一、 核心设计思想:什么是 ZSet 延迟队列?

Redis 的 ZSet 是一个有序集合 。它里面的每一条数据都会关联一个分数(Score),Redis 会自动根据分数从小到大给数据排好序。

在延迟队列场景中,我们做了一个巧妙的映射:

  • 数据内容(Member) :代表你要处理的任务(比如:订单 ID order_id_10086)。
  • 分数(Score) :代表这个任务的具体执行时间戳 (即 当前时间戳 + 延迟秒数)。

因为 ZSet 会自动排序,所以最先到期的任务,永远排在队列的最前面


二、 运行流程:存 与 取

1. 生产者:存入任务(投递消息)

当用户下单后,系统需要开启一个"10秒后未支付自动取消订单"的延迟任务。假设当前时间戳是 1700000000

生产者计算出执行时间:1700000000 + 10 = 1700000010

然后使用 ZADD 命令把任务塞进名为 delay_queue 的 ZSet 中:

sql 复制代码
ZADD delay_queue 1700000010 "order_id_10086"
2. 消费者:取出任务(轮询消费)

消费者(后台线程)会像一个定时闹钟一样,每隔 1 秒去 Redis 里巡检一次,看看有没有到期的任务。它使用的核心命令就是 ZRANGEBYSCORE

假设现在时间走到了 1700000015(距离下单过去了 15 秒),消费者发起查询:

sql 复制代码
ZRANGEBYSCORE delay_queue 0 1700000015 LIMIT 0 1

三、 深度理解:ZRANGEBYSCORE 在这里起什么作用?

这行命令的字面意思是:"去 delay_queue 里,把分数在 01700000015 之间的任务捞出来,但我只要第 1 条。"

对应到延迟队列的业务逻辑,参数拆解如下:

  • 0(时间下限) :因为时间戳是个递增的正整数,写 0 代表从最早、最老的时间开始算起,确保那些过去已经超时的任务不会被漏掉。
  • 1700000015(时间上限 = 当前时间戳) :这是最关键的限制。限制上限为"当前时间",意味着只有"执行时间 ≤ \le ≤ 当前时间"的任务才符合条件。那些还没到期的任务(比如要求在第 150 秒执行),因为分数大于 125,就会被直接过滤掉。
  • LIMIT 0 1(数量限制) :类似于 SQL 的 LIMIT 1。意思是虽然满足到期条件的可能有很多条,但我一次只取最该执行的那 1 条。这能有效防止高并发下多个消费者同时抢到大量重复任务。

四、 生产环境必须注意的"隐患"

在了解了 ZRANGEBYSCORE 的作用后,你会发现一个问题:它只是把数据"读"了出来,但并没有从 Redis 里"删掉"

如果两个消费者同时执行了上面那条命令,它们会同时拿到 order_id_10086,这就导致了重复消费

工业级的解决方案:
  1. Lua 脚本(推荐) :将 ZRANGEBYSCORE(查询)和 ZREM(删除)打包写进一个 Lua 脚本中。因为 Redis 执行 Lua 脚本是原子的,能够保证"谁先查到,谁就立马删掉",别人绝对抢不走。
  2. ZPOPMIN 命令(Redis 5.0+):直接弹出队列中分数最小的元素。可以先通过该命令弹出,并在代码中判断弹出的那个值是否小于当前时间,如果没到期再塞回去(或者配合其他逻辑),从而避免了重复消费。
相关推荐
无小道5 小时前
Redis——string类型相关指令
redis·指令·string
码云骑士6 小时前
Redis 入门实战:从 NoSQL 概念到安装与基础操作详解(一)
数据库·redis·缓存
Tirzano8 小时前
批量查询在线成员对应节点redis
数据库·redis·junit
wljt9 小时前
Redis的5种数据类型
数据库·redis·缓存
敖正炀9 小时前
分布式锁与 Redisson 深度:续期、红锁与无锁化
redis
燕-孑10 小时前
redis详解-进阶
数据库·redis·缓存
phltxy11 小时前
Redis 缓存
数据库·redis·缓存
小碗羊肉11 小时前
【Redis | 第一篇】Redis常见命令
数据库·redis·缓存
Devin~Y11 小时前
大厂Java面试实战:Spring Boot微服务、Redis缓存、Kafka消息队列与Spring AI RAG
java·spring boot·redis·kafka·mybatis·spring mvc·hikaricp