Redis可以设置某个键的过期时间这个我们都知道,可是你知道怎么过期删除Hash键中某个子键吗?

Redis中可没有直接提供这样设置Hash键的子键的过期方法,但是我们可以自己用Redis实现这个需求。

这里我们需要借用Redis中另外一个数据结构,那就是ZSET。我们在往Hash键中增加一个子键的时候,同时把该子键将要过期的时间戳当做score放进一个ZSET数据结构的键中,在用一个定时任务去定时扫描ZSET键,获取键中小于当前时间的子键,然后就可以根据这样获得的子键去Hash键中删除对应的子键了。

这样说可能有点抽象,我们用一个具体的例子来说明一下。

假如我们有一个这样的Hash键:CallRecord,然后里面存储的hkey是一个电话号码,value是这个电话号码的呼叫状态。然后有这样一个ZSET:CallRecord:ExpireAt,只要我们每产生一个呼叫记录,就把电话号码当做子键,对应电话号码相应的过期时间当做score塞进这个ZSET。这个过期时间可以是当前时间戳加上一个时间段,比如12小时。然后我们写一个定时任务,每分钟或者每秒(这个频率可以自己设置)扫描这个CallRecord:ExpireAt,利用Redis的方法rangeByScore(也就是选出小于某个分数的子键集合),这里我们就相当于选择小于当前时间的子键集合,这样过期的子键我们就知道了,然后遍历这个集合,用Hash的删除方法去一个一个删除就可以了(无奈redis没有直接删除集合中的元素的方法)。

下面直接上代码:

// 添加记录的方法
public void addRecordNew(CallRecord callRecord) {
  String callNumber = callRecord.getCallNumber();
  redisTemplate.opsForHash().put("CallRecord", callNumber, JSON.toJSONString(CallRecord));

  redisTemplate.opsForZSet().add("CallRecord:ExpireAt", callNumber, System.currentTimeMillis() + 12 * 60 * 60 * 1000);
}

// 定时删除的方法
@Scheduled(fixedDelay = 60000)
public void deleteExpireCallRecord() {
        long currentTimeMillis = System.currentTimeMillis();
        String key = "CallRecord:ExpireAt";
        Set<String> expireKeys = redisTemplate.opsForZSet().rangeByScore(key, 0, currentTimeMillis);
        if (CollectionUtils.isEmpty(expireKeys)) {
            log.info("[expire task] 没有需要删除的子键!");
            return;
        }
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, currentTimeMillis);

        for (String expireKey : expireKeys) {
            Long delete = redisTemplate.opsForHash().delete("CallRecord", expireKey);
            log.info("[expire task] 删除{}里面的子键: {}, delete = {}", "CallRecord", expireKey, delete);
        }
 }

这里使用ZSET还有一个好处就是可以利用rangeByScore直接拿到超时的子键集合,而不用遍历整个集合比对每个元素的超时时间是否比小于当前时间,所以时间复杂度也会降低,性能可以得到相应提升。

好了,超时删除Hash里面子键的方已经介绍完毕,不知道你有没有发现我们可以用这种方法来干一件“大事”,那就是原本为了使用Redis自带的超时机制而不得不用String类型来定义各种字段或实体的方法终于可以摒弃掉了,因为那样太浪费内存了,你可能要为了同一类型的字段或实体定义不同的键,而键在Redis中也是占用存储空间的。而如今你学会了我这个方法,那么就可以把同一种类型的字段或者实体用一个Hash类型的键来表示,不同的实体用Hash里面的子键表示,超时删除就利用ZSET的rangeByScore方法来获取超时的子键然后删除。这样一个和之前一模一样的功能就可以通过更节省内存的方式实现。



技术分享     

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!