因为Redis是纯内存操作,所以在Redis中创建的键一般都会带有过期时间,以此来保证内存中存储数据的时效性。这篇文章我们就来讲解一下Redis中的过期策略与内存淘汰策略。
如何设置Redis中键的过期时间?
Redis提供了4个命令来设置键的过期时间:
(1)EXPIRE<key><ttl>:表示将键key的生存时间设置为ttl秒。
(2)PEXPIRE<key><ttl>:表示将键key的生存时间设置为ttl毫秒。
(3)EXPIREAT<key><timestamp>:表示将键key的生存时间设置为timestamp所指定的秒数时间戳。
(4)PEXPIREAT<key><timestamp>:表示将键key的生存时间设置为timestamp所指定的毫秒数时间戳。
在Redis的内部实现中,前面三个设置过期时间的命令最后都会转换成最后一个PEXPIREAT命令来完成。
Redis中还提供了移除键的过期时间和查询键的剩余时间的操作。
(1)PERSIST<key>:表示将key的过期时间移除。
(2)TTL<key>:以秒的单位返回键key的剩余生存时间。
(3)PTTL<key>:以毫秒的单位返回键key的剩余生存时间。
Redis如何判定键是否过期?
在Redis内部,每当我们设置一个键的过期时间时,Redis就会将该键带上过期时间存放到一个过期字典中。当我们查询一个键时,Redis便首先检查该键是否在过期字典中,如果在,那就获取其过期时间。然后将过期时间和当前系统时间进行比较,如果比系统时间要大,则说明键还没有过期;反之,则说明键已经过期。
Redis中过期键的删除策略是什么?
首先介绍一下常见的3种过期删除策略:
(1)定时删除策略
当我们在设置键key的过期时间的同时,我们创建一个定时器,让定时器在过期时间到来时立刻执行键的删除操作。这种策略的优点是,对内存是最友好的,能够保证键key只要一过期就能够立即从内存中移除。缺点是对CPU最不友好,在过期的键比较多的时候,删除键会占用大量的CPU时间,从而使服务器的响应时间变长,吞吐量下降。
(2)惰性删除策略
当我们在设置键key的过期时间后,不再去管它,当需要使用该key时,我们再检查其是否过期,如果过期则执行删除操作,反之则正常执行其余操作。这种策略的优点是对CPU友好,我们只在使用到key的时候才去检查其过期时间和执行过期删除操作,对于一些过期但没有被使用到的key,不会立即执行删除操作,从而不会浪费大量时间在key的过期检查上。缺点是,对内存不友好,如果一个键已经过期了又长时间没有被使用,那么这个键会一直占用内存的空间,如果这样的键越来越多,就会造成大量内存泄露。
(3)定期删除策略
每隔一段时间,我们就对一些key进行过期检查,删除里面已经过期的key。这种策略的优点是可以通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响,同时也可以定期清除内存中过期的键,防止内存泄漏。缺点是难以确定删除操作执行的时长和频率。如果执行的频率太大,那就和定时删除策略的缺点一样,会占用大量CPU的时间;如果执行的频率太小,那就和惰性删除策略的缺点一样,会造成内存的大量泄露。还有一点,在获取某个键时,如果某个键的过期时间已经到了,但是还没有执行定期删除,那么就会返回这个键的值,这是在实际业务上不能忍受的错误。
介绍了以上三种删除策略后,我们发现各有优缺点,所以Redis使用惰性删除策略和定期删除策略配合使用来作为过期键的删除策略。
Redis中的惰性删除策略由db.c/expirelfNeeded函数实现,所有键读写命令执行之前都会调用expirelfNeeded函数对其进行检查,如果过期,则删除该键,并执行键不存在的操作;未过期则不作任何操作,继续执行原有的命令。
Redis中定期删除策略由redis.c/activeExpireCycle函数实现,函数以一定的频率运行,每次运行时,都从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中的过期键。Redis在执行定期删除策略时,并不是一次运行就检查所有的库,所有的键,而是随机检查一定数量的键。
Redis中内存淘汰策略有哪些?
虽然Redis中使用了键的过期策略来清理出内存空间,但是当Redis中存入大量键时,可能内存空间会不够用,这个时候只靠键的过期策略难以应对,因此需要有对应的内存淘汰策略。
当Redis的内存超过最大允许内存的内存之后,Redis会触发内存淘汰策略,淘汰一些不常用的数据,以保证Redis的正常运行。
Redisv4.0之前提供了6种内存数据淘汰策略:
(1)volatile-lru:在设置过过期时间的key中,利用LRU算法淘汰一部分。LRU算法是淘汰那些最近最少被使用过的内存数据。
(2)allkeys-lru:在内存的全部key中,利用LRU算法淘汰一部分。
(3)volatile-ttl:从已经设置过过期时间的数据中(server.db[i].expires)挑选将要过期的数据进行淘汰。
(4)volatile-random:从已经设置过过期时间的数据中(server.db[i].expires)随机挑选一部分进行淘汰。
(5)allkeys-random:从内存的全部key中,随机淘汰一部分。
(6)no-eviction:当内存不足时,新的写入操作会引发报错。
Redisv4.0之后又添加了2种内存淘汰策略:
(1)volatile-lfu:从已经设置过过期时间的数据中(server.db[i].expires)挑选最不经常使用的数据进行淘汰,也就是保留下来的key都是使用频率相对比较高的。
(2)allkeys-lfu:从内存的全部key中挑选最不常使用的数据进行淘汰。
Redis中的内存淘汰策略可以通过配置文件来更改。默认的是no-eviction(实际业务中不会使用),实际业务中最常使用的是allkeys-lru内存淘汰策略。