Redis学习总结(10)之过期删除策略和内存淘汰机制

     我们知道,redis中缓存的数据是有过期时间的,当缓存数据失效时,redis会删除过期数据以节省内存,那redis是怎样删除过期数据的?删除过期数据的策略是什么?这就是我们今天要讨论的第一个问题:Redis过期策略。

        redis为什么这么快,原因之一就是Redis操作都是基于内存的,既然是基于内存的,而内存的大小是有限的,当内存不足或占用过高时,怎么办?这就是我们今天要讨论的第二个问题:Redis的内存淘汰机制。


一、过期删除策略

        设置key的过期时间,超过时间后,将会自动删除该key。在Redis的术语中一个key的相关超时是不确定的。

        Redis在设置缓存数据时指定了过期时间,到了过期时间数据就失效了,那Redis是怎么处理这些失效的数据的呢?这就用到了Redis的过期策略——"定期删除+惰性删除" 。

        1.1定期删除

        定期删除是指Redis默认每隔 100ms 就 随机抽取 一些设置了过期时间的key,检测这些key是否过期,如果过期了就将其删除。

        ※ 100ms怎么来的?在Redis的配置文件redis.conf中有一个属性"hz",默认为10,表示1s执行10次定期删除,即每隔100ms执行一次,可以修改这个配置值。    

         ※ 随机抽取一些检测,一些是多少?同样是由redis.conf文件中的maxmemory-samples属性决定的,默认为5。

        ※ 为什么是随机抽取部分检测,而不是全部?因为如果Redis里面有大量key都设置了过期时间,全部都去检测一遍的话CPU负载就会很高,会浪费大量的时间在检测上面,甚至直接导致redis挂掉。所有只会抽取一部分而不会全部检查。

        正因为定期删除只是随机抽取部分key来检测,这样的话就会出现大量已经过期的key并没有被删除,这就是为什么有时候大量的key明明已经过了失效时间,但是redis的内存还是被大量占用的原因 ,为了解决这个问题,Redis又引入了“惰性删除策略”。

        1.2惰性删除

        惰性删除不是去主动删除,而是在你要获取某个key 的时候,redis会先去检测一下这个key是否已经过期,如果没有过期则返回给你,如果已经过期了,那么redis会删除这个key,不会返回给你。

         "定期删除+惰性删除"就能保证过期的key最终一定会被删掉 ,但是只能保证最终一定会被删除,要是定期删除遗漏的大量过期key,我们在很长的一段时间内也没有再访问这些key,那么这些过期key不就一直会存在于内存中吗?不就会一直占着我们的内存吗?这样不还是会导致redis内存耗尽吗?由于存在这样的问题,所以redis又引入了“内存淘汰机制”来解决。


二、内存淘汰策略

        2.1Maxmemory配置指令 

        maxmemory配置指令用于配置Redis存储数据时指定限制的内存大小。通过redis.conf可以设置该指令,或者之后使用CONFIG SET命令来进行运行时配置。

        例如为了配置内存限制为100mb,以下的指令可以放在redis.conf文件中。

maxmemory 100mb

        设置maxmemory为0代表没有内存限制。对于64位的系统这是个默认值,对于32位的系统默认内存限制为3GB。当指定的内存限制大小达到时,需要选择不同的行为,也就是策略。 Redis可以仅仅对命令返回错误,这将使得内存被使用得更多,或者回收一些旧的数据来使得添加数据时可以避免内存限制。

        2.2回收策略

          内存淘汰机制就能保证在redis内存占用过高的时候,去进行内存淘汰,也就是删除一部分key,保证redis的内存占用率不会过高,那么它会淘汰哪些key呢?Redis目前共提供了8种内存淘汰策略,含Redis 4.0版本之后又新增的两种LFU模式:volatile-lfu和allkeys-lfu。

no-eviction 当内存不足以容纳新写入数据时,新写入操作会报错,无法写入新数据,一般不采用。
allkeys-lru 当内存不足以容纳新写入数据时,移除最近最少使用的key,这个是最常用的。
allkeys-random 当内存不足以容纳新写入的数据时,随机移除key。
allkeys-lfu 当内存不足以容纳新写入数据时,移除最不经常使用的key。
volatile-lru 当内存不足以容纳新写入数据时,在设置了过期时间的key中,移除最近最少使用的key。
volatile-random 内存不足以容纳新写入数据时,在设置了过期时间的key中,随机移除某个key 。
volatile-lfu 当内存不足以容纳新写入数据时,在设置了过期时间的key中,移除最不经常使用的key。
volatile-ttl 当内存不足以容纳新写入数据时,在设置了过期时间的key中,优先移除过期时间最早的key(过期时间最早即剩余存活时间最短)。

   其实我们记住他的淘汰策略,主要从两个维度去记住,首先是allkeys,针对对象就是所有的key,那她相对的就是volatitle就是针对设置了过期时间的key,其次就是lru跟lfu,lru是最近最少使用,lfu就是最近最少访问的。

        lfu相对于lru的区别,其实就是lfu比lru多了一个计数器,举个例子,如果触发内存淘汰策略,如果A距离的时间更久,但是实际上A使用的频率比B还要高,所以合理的淘汰策略就应该淘汰B,所以LFU就会淘汰B,而LRU就会淘汰A。

        ※ 内存淘汰策略的配置:内存淘汰机制由redis.conf配置文件中的maxmemory-policy属性设置,没有配置时默认为no-eviction模式。

        ※ 淘汰策略的执行过程

  • 客户端执行一条命令,导致Redis需要增加数据(比如set key value);
  • Redis会检查内存使用情况,如果内存使用超过 maxmemory,就会按照配置的置换策略maxmemory-policy删除一些key;
  • 再执行新的数据的set操作

2.3LRU&LFU算法

        在上面的8种Redis内存淘汰机制中有xxxLru、xxxLfu模式,这些模式的实现其实是基于LRU或LFU算法实现的,在面试的时候面试官可能会让你简单的写一段LRU算法实现的伪代码。

     标准LRU算法是这样的:它把数据存放在链表中按照“最近访问”的顺序排列,当某个key被访问时就将此key移动到链表的头部,保证了最近访问过的元素在链表的头部或前面。当链表满了之后,就将"最近最久未使用"的,即链表尾部的元素删除,再将新的元素添加至链表头部。

         因为标准LRU算法需要消耗大量的内存,所以Redis采用了一种近似LRU的做法:给每个key增加一个大小为24bit的属性字段,代表最后一次被访问的时间戳。然后随机采样出5个key,淘汰掉最旧的key,直到Redis占用内存小于maxmemory为止。其中随机采样的数量可以通过Redis配置文件中的 maxmemory_samples 属性来调整,默认是5,采样数量越大越接近于标准LRU算法,但也会带来性能的消耗。

         在Redis 3.0以后增加了LRU淘汰池,进一步提高了与标准LRU算法效果的相似度。淘汰池即维护的一个数组,数组大小等于抽样数量 maxmemory_samples,在每一次淘汰时,新随机抽取的key和淘汰池中的key进行合并,然后淘汰掉最旧的key,将剩余较旧的前面5个key放入淘汰池中待下一次循环使用。假如maxmemory_samples=5,随机抽取5个元素,淘汰池中还有5个元素,相当于变相的maxmemory_samples=10了,所以进一步提高了与LRU算法的相似度。

Guess you like

Origin blog.csdn.net/jokeMqc/article/details/121394084