Redis过期删除策略与内存淘汰策略

41 阅读7分钟

什么是过期删除策略?什么是内存淘汰策略?有时候容易将这两个搞混,但是我们从字面上去理解就可以很好的区分开了。一个是过期删除也就是到达了过期时间,将key去删除而内存淘汰则就是内存满了存不进去了去进行内存的优化调整,淘汰掉一些数据。

如果你是第一次了解或者温习的,让我们开始吧!

Redis过期删除策略

我们知道的是,Redis可以对我们存储的Key进行过期时间的时间,当到达了Key的过期时间后,Redis会进行将对应的Key进行删除,那么就要有对应的机制进行删除的操作,而这个工作就是过期键值删除策略

过期时间的设定

我们先来简单了解一下键值的过期时间设置和Redis是如何判定Key已经过期了

先来看看对Key设置过期时间的命令。设置key过期时间的命令一共有4个

expire <key> <n> 设置key在n秒后过期
pexpire <key> <n> 设置key在n毫秒后过期
expireat <key> <n> 设置key在某个时间戳(精确到秒)之后过期
pexpireat <key> <n> 设置key在某个时间戳(精确到毫秒)之后过期

那么Redis是如何判断key已过期了?

在前面的设定key过期时间之后,Redis会把该key带上过期时间存储到一个过期字典中(expires dict) 中,换句话说,也就是过期字典保存了数据库中所有key的过期时间

过期字典中存储的数据结构中的有一个key指针指向的是某个键值对象还有一个longlong类型的value保存的是key的过期时间。

而字典实际上是哈希表,可以让我们以O(1)的时间复杂度度来快速查找

当我们查找一个key的时候,Redis会先检查对应的key是否存在于过期字典中:

  • 如果不存在,则正常读取键值
  • 如果存在,会先判断过期时间是否大于当前系统时间,如果大于说明没有过期,反之就判定该key过期了

哦了,聊了这么多。我们终于要进入正题了

过期删除策略都有哪些

Redis比较常见的过期删除策略有下面三种

  • 定期删除
  • 惰性删除
  • 定时删除

接下来,我们来分别看看对应的策略都是怎么样的

定期删除

定期删除策略:每隔一段时间随机拿取一批key检查其是否过期,如果过期了就进行删除操作

优点是:通过限制删除操作的执行时长和频率,来减少删除操作对CPU的影响,同时可以删除一部分过期的数据来减少过期健对空间的无效占用

缺点是:内存清理方面,没有定时删除的效果好,且占用的系统资源比惰性删除的多。而且不好把握删除的执行时长和频率,如果执行的太频繁对CPU不友好,执行的太少就变的和惰性删除差不多

惰性删除

惰性删除策略:不进行主动的删除键值,而是等到请求到数据库读取对应的key时,再判断key是否过期,如果过期就删除并且返回null,反之就正常返回数据

优点是:对CPU极度友好,因为是等访问时才会去判断key是否过期,因此占用的系统资源很少

缺点是:会造成一定程度的内存资源浪费,假设大量数据过期很久且很久没去访问则会长时间保留在数据库中,对内存很不友好

定时删除

定时删除策略:在设置key的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理其自动执行key的删除操作

优点是:可以保证过期key会被尽快删除,内存也可以尽快被释放,所以这个策略对内存是最友好的

缺点是:如果当大量的key过期时间堆积到一起的话,对CPU的压力比较大。会对服务器的响应时间和吞吐量造成影响

Redis的过期策略是什么

前面的讲解了三种过期删除策略,都有对应的优缺点,单独使用的话,不可避免的出现问题。

所以Redis使用的策略是惰性删除+定期删除这两个策略配合和使用,以求在合理使用CPU时间和避免内存;浪费之前取得平衡

Redis中的惰性删除

Redis中的惰性删除由db.c文件中的expireIfNeeded函数实现

int expireIfNeeded(redisDb *db, robj *key) {
    if (!keyIsExpired(db,key)) return 0;
    return server.lazyfree_lazy_expire ? abAsyncDelete(db,key): deSyncDelete(db,key);
}

Redis在访问或者修改key之前都会调用expireIfNeeded函数进行key的过期检查

  • 如果过期就删除key,选择异步还是同步看参数配置决定了
  • 如果没有过期就不进行处理正常返回数据给客户

Redis中的定期删除

上面我们介绍了定期删除,就是每隔一段时间随机从数据库中拿取一批数据进行检查,过期了再删除

那么我们就需要确定对应的检查的间隔时间和拿取的数量了

在Redis中默认的是每秒进行10次的检查,也就是配置值中的10hz,redis.conf进行配置默认值就是10hz

注意一下每次检查是随机抽取一批数量的key进行检查而不是全部的key

对于对应的随机抽查数量是写死的 在代码中就是20个也就是说数据库每轮随机抽取20个key进行判断是否过期

ok让我们完整的来看看Redis的定期删除的流程

  1. 从过期字典中随机抽取20个key
  2. 检查20个key是否过期,并删除已过期的key
  3. 如果本轮检查的key过期数量超过5个就再进行新的一轮 如果没有就结束

内存淘汰策略

前面我们已经介绍完了过期删除策略,是删除已经过期的key。而当Redis的运行内存已经超过了原先设置的最大内存之后则就会使用内存淘汰策略来删除符合条件的key,以确保Redis可以高效的进行。

设置Redis的最大内存运行内存

在配置文件中可以通过参数maxmemory来设定最大运行内存,要注意只有在Redis的运行内存达到了我们设置的最大运行内存才会触发内存淘汰策略。不同位数的操作系统,maxmemory的默认值是不一样的

Redis内存淘汰策略有哪些

Redis中内存淘汰策略有八种,我们可以分为不进行数据淘汰和进行数据淘汰两个大类

不进行数据淘汰

noeviction(Redis3.0之后默认的内存淘汰策略):不会进行数据的内存淘汰,当运行内存超过最大设置内存时,不淘汰任何数据,这时如果有新的数据要写入则通知禁止写入,不淘汰任何数据。如果只是单纯的查询或者删除的话可以正常操作

进行数据淘汰

在进行数据淘汰的范围内又可以分为两大类(在设置了过期时间的数据中进行淘汰)和(在所有数据范围内进行淘汰)

在设置了过期时间的数据中进行淘汰:

  • volatile-random: 随机淘汰设置了过期时间的任意键值
  • volatile-ttl:优先淘汰更早过期的键值
  • volatile-lru:淘汰所有设置了过期时间的键值中,最久未使用的键值
  • volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值

在所有数据范围内进行淘汰:

  • allkeys-random:随机淘汰任意键值;
  • allkeys-lru:淘汰整个键值中最久未使用的键值
  • allkeys-lfu:淘汰整个键值中最少使用的键值