Redis实现去重任务队列

前情提要:一点小小的不完善的方案的思考和设计,不对的地方或是更好的方案欢迎大佬们在评论区讨论~

需求背景:

在Redis里使用List数据结构做任务队列,但是有的时候任务可能会重复添加,所以需要进行去重。 队列需要有优先级,尽量减少Redis操作次数。

尝试方案

目前能够想到的方案有以下几种:

1. List 不重复添加

只使用List,将新的任务加入任务队列之前先判断该任务是否已经存在。 添加任务: 在加入任务队列之前获取List的全部元素,如果新的任务在任务队列里已经存在了,就直接返回;否则的话,将新的任务添加到List里。 获取名字为task_queue 的全部元素

powershell 复制代码
LRANGE task_queue 0 -1

将元素插入队尾

shell 复制代码
LPUSH task_queue val1

执行任务: 从队首取任务。

弹出队首元素

shell 复制代码
LPOP task_queue 

2.List 重复添加

只使用List,但是在出队的时候删除相同的元素。 添加任务: 往List里放数据 将元素插入队尾

shell 复制代码
LPUSH task_queue val1

执行任务: 从队首取数据,并且取任务的时候将相同的元素删除。 弹出队首元素

shell 复制代码
LPOP task_queue 

移除队列里和val1值相等的元素 LREM说明

shell 复制代码
 LREM task_queue 0 val1

3. List + Set/Hash/Bloom Filter

Set/Hash/Bloom Filter 的作用都是哈希表,里面存储任务的唯一标识,确保任务不会重复,下面以Set为例。因为从功能性而言,List里存放的是任务的基础信息,除了唯一标识外还有其他额外的业务信息,Set里只用来确保任务不会重复,无需存储额外信息。当然也可以List里只存储任务的唯一标识,Hash里存放额外的业务信息。至于布隆过滤器,一般用于数据量很大的场景,这里的设计都是基于100以内的小排队模式。 添加任务: 先判断该任务在Set里是否存在,如果存在的话,就直接返回;否则,将该任务添加到List和Set里。 判断某个元素val1是否在集合task_set里存在

shell 复制代码
SISMEMBER task_set val1

将元素插入队尾

shell 复制代码
LPUSH task_queue val1

向Set添加一个元素

shell 复制代码
SADD task_set val1

执行任务: 先从List里取数据,判断其在Set里是否存在,如果不存在的话,说明是非法情况,可以给予报警或其他逻辑处理;存在的话,将该元素从Set里删除。 判断某个元素val1是否在集合task_set里存在

shell 复制代码
SISMEMBER task_set val1

弹出队首元素

shell 复制代码
LPOP task_queue 

4. sorted set (ZSet)

将任务表示作为成员,分数是任务执行时间或是优先级或是其他规则作为分数,这样可以对任务进行排序。 添加任务:

先判断在ZSet里是否存在,如果存在的话跳过或执行业务代码;不存在的话将其添加到ZSet里。 判断某个元素是否存在,ZSet没有直接的api,需要借助其他方式来判断,比如通过返回有序集中元素的分数值可以判断

shell 复制代码
ZSCORE task_zset val1

添加元素到ZSet里

shell 复制代码
ZADD key task_zset val1

执行任务: 按排序规则取任务 取分数最大的元素 ZRANGE说明

shell 复制代码
ZRANGE task_zset 0 0 WITHSCORES

移除分数最大的元素 ZREMRANGEBYRANK 说明

shell 复制代码
ZREMRANGEBYRANK task_zset 0 0

5. Lua脚本

使用Lua脚本实现原子性操作。可以在添加新任务时执行 Lua 脚本,该脚本会检查任务是否已存在、更新状态等。使用 Lua 脚本的好处是可以减少网络往返次数和提高执行效率。

6. 开源库或框架

Celery:是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度。 源码 介绍

RQ(Redis Queue) 源码 介绍

问题思考

以上方案存在的问题在于,如果一个任务执行的时间较长,取任务的时候就已经将其从任务队列里删除了,但是当重复任务到达的时候,上一个任务其实并没有执行完,此时任务队列里也没有这个任务,还是会被添加到队列里。 目前能够想到的解决方案,第一个是双List ,一个List里存放待执行的任务,一个List里存放正在执行的任务,添加任务的时候同时检查这两个List是否有任务。第二个是List+Set方案里,修改Set里数据表示的含义,待执行和正在执行的任务都存在Set里,也就是执行任务的时候,先从List队首取元素,执行任务,然后再将Set中的元素删除。

相关推荐
mikey棒棒棒2 小时前
Redis——优惠券秒杀问题(分布式id、一人多单超卖、乐悲锁、CAS、分布式锁、Redisson)
数据库·redis·lua·redisson·watchdog·cas·并发锁
Asthenia04123 小时前
浏览器缓存机制深度解析:电商场景下的性能优化实践
后端
databook4 小时前
『Python底层原理』--Python对象系统探秘
后端·python
超爱吃士力架5 小时前
MySQL 中的回表是什么?
java·后端·面试
追逐时光者5 小时前
Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
后端·.net
Familyism6 小时前
Redis
数据库·redis·缓存
苏三说技术6 小时前
10亿数据,如何迁移?
后端
bobz9656 小时前
openvpn 显示已经建立,但是 ping 不通
后端
customer087 小时前
【开源免费】基于SpringBoot+Vue.JS个人博客系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
qq_459238497 小时前
SpringBoot整合Redis和Redision锁
spring boot·redis·后端