使用 Redis 的 zset 实现延时队列

Java 延时队列DelayQueue实现原理及Demo

在上一篇文章中,我们使用jdk自带的DelayQueue延时队列写了一个小demo,现在来看看使用redis的zset有序集合是怎么实现延时队列的。在实现之前,先把zset相关的指令了解一下。

一、zset集合
zset是一个有序集合,通过排序属性score进行排序;也就是说每个存储元素都是由两个元素组成,一个是有序值,另一个是排序值。

二、zset相关指令

#往集合里添加元素
zadd 有序集合key 排序值 有序值 排序值 有序值
# 取出有序集合里的所有元素
zrange 有序集合key 0 -1
# 根据score进行删除
zremrangebyscore 有序集合key 开始score 结束score

参考文档:https://redis.io/docs/manual/data-types

三、使用zset实现延迟队列

3.1 使用当前时间加上随机秒数作为score分值,往key值为AA的zset中添加数据;

@Autowired
private RedisTemplate redisTemplate;

// 日期格式化,精确到时分秒  
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

/**
 * 60秒执行一次
 */
@Scheduled(cron = "*/60 * * * * ?")
public void initKeys() {      
    // zset数据添加
    ZSetOperations zset = redisTemplate.opsForZSet();
    Random random = new Random();
    int score = 0;
    Long scheduleTime = 0L;
    for(int i=0;i<100;i++){
      // 取值1到100的随机数
      score = random.nextInt(100);  
      // 设置score,在当前时间上加score秒
      scheduleTime = System.currentTimeMillis() + (1000) * score;
      log.info("AA{}{},过期时间:{}",i,score, format.format(new Date(scheduleTime)));
      zset.add("AA", "AA" + i + score, scheduleTime);
    }      
}

3.2 根据当前时间取出zset中的过期数据并删除;

// 延迟任务处理
ZSetOperations zset = redisTemplate.opsForZSet();
// TimerTask 定时任务
new Timer().schedule(new TimerTask() {
    @Override
    public void run() {
        Long now = System.currentTimeMillis();
        // 取出小于等于当前时间的元素
        Set set = zset.rangeByScore("AA", 0, now);
    if(!set.isEmpty()){
      log.info("当期时间:{},过期数据={}", format.format(new Date(now)), set.toArray());
      // zset中删除过期的元素
      zset.removeRangeByScore("AA", 0, now);
    }
    }
},10,100);// 延时10毫秒执行,每隔100毫秒执行一次,1秒执行10次做到尽量精准
 
 

运行结果:


最后总结

使用redis的有序集合zset实现延迟队列,核心就在score分值上,通过当前时间加上延迟时间作为score,使用zremrangebyscore 命令取出0到当前时间(long型)的元素,能够取出来的就是过期的元素。当然,这个算法也存在不足之处,在取值的时候,采用了轮询,会白白地浪费一部分性能。当然我们主要是了解这个算法是怎么实现的。

参考文档:
https://blog.csdn.net/u012791490/article/details/125243933
https://mp.weixin.qq.com/s/WymM9LDjEcNZDhx4e6nLkA
https://zhuanlan.zhihu.com/p/423916169

你可能感兴趣的:(使用 Redis 的 zset 实现延时队列)