redis数据库队列(list),集合(set)元素设置类似过期(expire)功能

**(2018-07-23更新:
本案例使用的解决方案以及代码,在较大数据量的操作情境下存在严重redis性能问题——大集合的一次性删除操作可能导致redis阻塞,正常业务将无法访问、操作redis。如果需要使用相关功能,请使用资料中的1方法或自行优化redis删除队列操作的执行时间)**

问题:

项目需要为每个用户维护一个列表,存放一些数据。列表中的值有过期时间,过期的值查询可以找到也可以找不到,还会有一个验证,所以无所谓。但是redis队列只有一个整体的过期功能,没有每个元素的单独过期功能,所以如果用户一直不停向队列塞东西,队列就会变的越来越大。这显然不合理。

资料:

https://stackoverflow.com/questions/16545321/how-to-expire-the-hset-child-key-in-redis

https://quickleft.com/blog/how-to-create-and-expire-list-items-in-redis/

查了一下资料,目前给队列、集合元素单独设置过期不可能做到(redis4.0.2)。但是有其他方法可以做到类似功能。

参考的一篇文章提出两种方法:

1.使用SortedSet,使用score参数代表unix时间,程序定期使用ZRANGEBYSCORE清除过期项

2.将集合拆分成多个按时间排序、自动过期的小集合

1方法显然更方便、高效,但是项目必须跑程序为每个集合定期维护,可能产生很多不必要的麻烦,所以我选择2解决方法。

解决方案:

(重要的话说三遍:这个解决方案中过期的值有可能被返回,过期的值有可能被返回,过期的值有可能被返回。如果需要返回确定不过期的值,请在value中加unix时间作验证)

使用一个工具类封装redis操作,自动进行redis集合的拆分和查询。直接上代码:

必须获得redis读、写、设置过期、查询key是否存在 函数

expire表示过期时间(秒); blockSize表示分块大小(秒),不能大于expire

package com.study.javaweb.test1.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * User: longjuanfeng Date: 2017-11-29
 */
public class RedisAutoExpireUtils<ValueType, SetResponse> {
    private Logger logger = LoggerFactory.getLogger(RedisAutoExpireUtils.class);

    private RedisSetter<ValueType, SetResponse> redisSetter;

    private RedisGetter<ValueType> redisGetter;

    private RedisExpire redisExpire;

    private RedisExists redisExists;

    private Integer expire;

    //default size is expire
    private Integer blockSize;

    public RedisAutoExpireUtils(RedisSetter<ValueType, SetResponse> redisSetter, RedisGetter<ValueType> redisGetter, RedisExpire redisExpire, RedisExists redisExists, Integer expire) throws Exception {
        this(redisSetter, redisGetter, redisExpire, redisExists, expire, expire);
    }

    public RedisAutoExpireUtils(RedisSetter<ValueType, SetResponse> redisSetter, RedisGetter<ValueType> redisGetter, RedisExpire redisExpire, RedisExists redisExists, Integer expire, Integer blockSize) throws Exception {
        if (blockSize > expire) {
            throw new Exception("blockSize should not larger than expire");
        }
        this.redisSetter = redisSetter;
        this.redisGetter = redisGetter;
        this.redisExpire = redisExpire;
        this.redisExists = redisExists;
        this.expire = expire;
        this.blockSize = blockSize;
    }

    public SetResponse setRedisValue(String key, ValueType value) {
        Integer nowTime = new Long(System.currentTimeMillis() / 1000).intValue();
        Integer blockTail = nowTime % blockSize;
        Integer stampPos = nowTime - blockTail;
        String timeStamp = String.valueOf(stampPos);
        logger.info("timeStamp of {} is {}", nowTime, timeStamp);
        String keyWithStamp = key + ":" + timeStamp;
        Boolean exists = redisExists.exists(keyWithStamp) == 1;
        SetResponse result = redisSetter.addValue(keyWithStamp, value);
        if (!exists) {
            Integer expireTime = expire + blockSize - blockTail;
            redisExpire.setExpire(keyWithStamp, expireTime);
            logger.info("set expire of {} is {}", nowTime, expireTime);
        }
        return result;
    }

    public ValueType getRedisValue(String key) {
        Integer nowTime = new Long(System.currentTimeMillis() / 1000).intValue();
        Integer checkBlockNum = expire / blockSize + 1;
        Integer blockTail = nowTime % blockSize;
        Integer stampPos = nowTime - blockTail;
        for (int i = 0; i < checkBlockNum; i++) {
            String timeStamp = String.valueOf(stampPos);
            logger.info("check timeStamp of {} is {}", nowTime, timeStamp);
  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值