Redis使用zset有序集合做延迟队列

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis使用zset有序集合做延迟队列相关的知识,希望对你有一定的参考价值。

参考技术A 把所有需要在未来执行的任务都添加到有序集合里面,并将任务的执行时间设置为分值,另外再使用一个进程来查找有序集合里面是否存在可以立即执行的任务,如果有的话,就从有序集合里面移除那个任务,并将它添加到适当的任务队列里面。

--出自《Redis实战》

创建函数 addFutureJob,负责将延迟任务添加到有序集合job中。

有序集合里存储的元素,可以使用json格式保存。

内部结构可以类似如下这种:

参数:
$job: 存储延迟任务的有序集合的名字,叫job

$queue: 当任务到达执行时间时,转存到具体的队列里执行

$fun: 负责执行的函数名称或匿名函数

$time: 延迟任务执行的具体时间

$parameter: 传递的参数

执行脚本后,将存储到job 这个有序集合里

另一个脚本中,读取job集合,检查是否有需要执行的任务

这个函数getQueue()的基本流程是这样:
(1)根据分数从小到大排列,读取第一个元素。如果元素不存在返回false
(2) 如果元素任务存在,并且它的分数(执行时间)小于等于当前时间,说明这个任务可以执行了。
(3) json转化成数组,读取任务的queue参数,将它添加到指定的队列里,然后从job中删除这个任务。
(4) 上述转移操作时,如果成功,记录日志。while继续循环检查job有序集合
(5) 如果转移操作失败,返回false
(6) 后续没有要执行的任务时,停止循环,返回false

此处循环读取也可以使用zrangeByScore()函数,根据分数范围进行读取返回集合内的指定元素。

Redis:有序集合类型zset实现原理

和上面的集合对象相比,有序集合对象是有序的。与列表使用索引下标作为排序依据不同,有序集合为每个元素设置一个分数(score)作为排序依据。

①、编码
有序集合的编码可以是 ziplist 或者 skiplist。

ziplist 编码的有序集合对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点保存元素的分值。并且压缩列表内的集合元素按分值从小到大的顺序进行排列,小的放置在靠近表头的位置,大的放置在靠近表尾的位置。

//操作
ZADD price 8.5 apple 5.0 banana 6.0 cherry

//存储顺序


skiplist 编码的有序集合对象使用 zet 结构作为底层实现,一个 zset 结构同时包含一个字典和一个跳跃表:

typedef struct zset{
     //跳跃表
     zskiplist *zsl;
     //字典
     dict *dice;
} zset;

字典的键保存元素的值,字典的值则保存元素的分值;跳跃表节点的 object 属性保存元素的成员,跳跃表节点的 score 属性保存元素的分值。

这两种数据结构会通过指针来共享相同元素的成员和分值,所以不会产生重复成员和分值,造成内存的浪费。

说明:其实有序集合单独使用字典或跳跃表其中一种数据结构都可以实现,但是这里使用两种数据结构组合起来,原因是假如我们单独使用 字典,虽然能以 O(1) 的时间复杂度查找成员的分值,但是因为字典是以无序的方式来保存集合元素,所以每次进行范围操作的时候都要进行排序;假如我们单独使用跳跃表来实现,虽然能执行范围操作,但是查找操作有 O(1)的复杂度变为了O(logN)。因此Redis使用了两种数据结构来共同实现有序集合。

②、编码转换
当有序集合对象同时满足以下两个条件时,对象使用 ziplist 编码:

1、保存的元素数量小于128;

2、保存的所有元素长度都小于64字节。

不能满足上面两个条件的使用 skiplist 编码。以上两个条件也可以通过Redis配置文件zset-max-ziplist-entries 选项和 zset-max-ziplist-value 进行修改。

以上是关于Redis使用zset有序集合做延迟队列的主要内容,如果未能解决你的问题,请参考以下文章

Redis使用bitmap、zset、hash、list等结构完成骚操作?

php+redis简易消息队列

七种武器:延迟队列的原理和实现总结

七种武器:延迟队列的原理和实现总结

七种武器:延迟队列的原理和实现总结

golang实现本地延迟队列