使用 Redis 的 zset 實(shí)現(xiàn)延時(shí)隊(duì)列

Java 延時(shí)隊(duì)列DelayQueue實(shí)現(xiàn)原理及Demo

在上一篇文章中,我們使用jdk自帶的DelayQueue延時(shí)隊(duì)列寫(xiě)了一個(gè)小demo,現(xiàn)在來(lái)看看使用redis的zset有序集合是怎么實(shí)現(xiàn)延時(shí)隊(duì)列的。在實(shí)現(xiàn)之前,先把zset相關(guān)的指令了解一下。

一、zset集合
zset是一個(gè)有序集合,通過(guò)排序?qū)傩詓core進(jìn)行排序;也就是說(shuō)每個(gè)存儲(chǔ)元素都是由兩個(gè)元素組成,一個(gè)是有序值,另一個(gè)是排序值。

二、zset相關(guān)指令

#往集合里添加元素
zadd 有序集合key 排序值 有序值 排序值 有序值
# 取出有序集合里的所有元素
zrange 有序集合key 0 -1
# 根據(jù)score進(jìn)行刪除
zremrangebyscore 有序集合key 開(kāi)始score 結(jié)束score

參考文檔:https://redis.io/docs/manual/data-types

三、使用zset實(shí)現(xiàn)延遲隊(duì)列

3.1 使用當(dāng)前時(shí)間加上隨機(jī)秒數(shù)作為score分值,往key值為AA的zset中添加數(shù)據(jù);

@Autowired
private RedisTemplate<String,Object> redisTemplate;

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

/**
 * 60秒執(zhí)行一次
 */
@Scheduled(cron = "*/60 * * * * ?")
public void initKeys() {      
    // zset數(shù)據(jù)添加
    ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
    Random random = new Random();
    int score = 0;
    Long scheduleTime = 0L;
    for(int i=0;i<100;i++){
      // 取值1到100的隨機(jī)數(shù)
      score = random.nextInt(100);  
      // 設(shè)置score,在當(dāng)前時(shí)間上加score秒
      scheduleTime = System.currentTimeMillis() + (1000) * score;
      log.info("AA{}{},過(guò)期時(shí)間:{}",i,score, format.format(new Date(scheduleTime)));
      zset.add("AA", "AA" + i + score, scheduleTime);
    }      
}

3.2 根據(jù)當(dāng)前時(shí)間取出zset中的過(guò)期數(shù)據(jù)并刪除;

// 延遲任務(wù)處理
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
// TimerTask 定時(shí)任務(wù)
new Timer().schedule(new TimerTask() {
    @Override
    public void run() {
        Long now = System.currentTimeMillis();
        // 取出小于等于當(dāng)前時(shí)間的元素
        Set<Object> set = zset.rangeByScore("AA", 0, now);
    if(!set.isEmpty()){
      log.info("當(dāng)期時(shí)間:{},過(guò)期數(shù)據(jù)={}", format.format(new Date(now)), set.toArray());
      // zset中刪除過(guò)期的元素
      zset.removeRangeByScore("AA", 0, now);
    }
    }
},10,100);// 延時(shí)10毫秒執(zhí)行,每隔100毫秒執(zhí)行一次,1秒執(zhí)行10次做到盡量精準(zhǔn)

運(yùn)行結(jié)果:


最后總結(jié)

使用redis的有序集合zset實(shí)現(xiàn)延遲隊(duì)列,核心就在score分值上,通過(guò)當(dāng)前時(shí)間加上延遲時(shí)間作為score,使用zremrangebyscore 命令取出0到當(dāng)前時(shí)間(long型)的元素,能夠取出來(lái)的就是過(guò)期的元素。當(dāng)然,這個(gè)算法也存在不足之處,在取值的時(shí)候,采用了輪詢,會(huì)白白地浪費(fèi)一部分性能。當(dāng)然我們主要是了解這個(gè)算法是怎么實(shí)現(xiàn)的。

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容