如何删除Redis Hash数据结构中的子键呢?

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没有直接删除集合中的元素的方法)。

下面直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 添加记录的方法
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方法来获取超时的子键然后删除。这样一个和之前一模一样的功能就可以通过更节省内存的方式实现。


如何删除Redis Hash数据结构中的子键呢?
https://www.chuckfang.com/2020/07/22/How-to-delete-redis-hash-hkey/
作者
方程
发布于
2020年7月22日
许可协议