Redis骚操作系列-如何让 Redis 的值过期

Redis 的过期时间设置是一个很常用的操作,毕竟 redis 不是用来做持久化存储的。

问题

Redis 自带的过期机制只能对 key 过期,而不能对具体的值过期,但是在某些特定的场景下需要对部分值过期。设想一个场景,有一篇文章,通过唯一的 id 进行标识。现在有个需求是要对每篇文章 15 分钟内的的阅读量进行统计,每分钟统计一次,然后查看文章阅读量的变化。所以数据形式大致如下:

1
[1, 5, 6, 10, 16 ...]

但是我只需要 15 分钟内的数据,要不然内存可能就不够用了,这个时候利用 key 过期的方式就无法满足需求了,所以可以利用 Redis 的一些特性来完成这个需求。

解决方法

Redis 的有序集合可以存储一系列有序的值,对于值可以指定 score,然后根据 score 来进行排序:

1
ZAAD key score value

然后可以通过 ZRANGEBYSCORE 来获取一定范围内 score 所对应的 value。

1
ZRANGEBYSCORE key score1 score2

可以利用上面的这个特性来完成需求。

可以把统计的文章的阅读数量通过如下的格式添加进行 Redis,以文章的 id 作为 key, 当前的时间戳作为 score,当前文章的阅读数作为 value。像下面这样:

1
2
#   文章id   时间戳    阅读数 
ZADD id 1577591750 54

然后要获取 15 分钟内的阅读数量统计,假设当前的时间戳是 now:

1
ZRANGEBYSCORE id now - 15 * 60 now

上面的操作就可以达到目的了,但是还是有点不完美,因为随着时间的推移,这个有序列表会越来越长,最后内存还是会被搞爆掉。所以在每次获取 15 分钟内的值时,还要把超过 15 分钟的值给删掉,这样就和过期的效果一样了:

1
ZRANGEBYSCORE id 0 now - 15 * 60

现在看起来比较完美了,即能完成需求,又能保住内存。但是还是存在一个小问题,假设这篇文章不再有人访问了,那么这个值就会永远停在内存中(假设不宕机)。所以保险一点,可以给这个 key 也设置一个过期的时间:

1
2
# 24 小时后整个 key 过期,内存回收
EXPIRE key 24* 60 * 60

现在果真完美了,即满足了要求,也保全了内存。不是我啰嗦,但是这个从功能的角度还是有点小问题,因为有序集合中存储的值只能是唯一的,要是两次统计的阅读量一样,就会有点小问题。这个也容易解决,把每个 value 设计成如下的形式就可以了:

1
2
3
4
{
value: numbers,
timestamp: 1577591750
}

这样一来,每个 value 都是独一无二的了,这样真的是完美了。示意图如下:

只有 黄色框内的值才是有效的,这个框就表示15分钟的有效时间。框外的就表示过期的 value。

© 2020 Rayjun    PowerBy Hexo