redis——内存满了应该怎么办?

我们的redis使用的时内存空间来存储数据的,但是内存空间毕竟有限,随着我们存储数据的不断增长,当超过了我们的内存大小时,redis会怎么处理呢?今天就来聊聊redis的缓存淘汰策略。

一、redis的缓存淘汰策略

在redis中,一种有8种对应的缓存淘汰策略

根据是否进行数据淘汰可以分为:不淘汰的数据策略(noeviction)和7种淘汰数据策略。

在淘汰的数据策略中,又可以根据淘汰数据的样本分为:在设置了过期时间的数据中进行淘汰数据的四种策略(volatile-ttl,volatile-radom,volatile-lru,volatile-lfu)和在全部数据中进行数据淘汰的三种策略(allkeys-radmom、allkeys-lru、allkeys-lfu)

  • volatile-ttl:表示在设置可过期时间的键值对中,根据过期时间的先后进行淘汰数据,越早被过期的数据,越先被淘汰。
  • volatile-random:从名字可以看出来,就是在设置了过期时间的键值对中,随机淘汰数据。
  • volatile-lru:会根据lru算法进行数据的淘汰
  • volatile-lfu:会根据lfu算法进行数据的淘汰
  • allkeys-random:在全部的键值对数据中,进行数据的随机淘汰。
  • allkeys-lru:在全部的键值对数据中,根据lru算法进行数据的淘汰。
  • allkeys-lfu:在全部的键值对数据中,根据lfu算法进行数据的淘汰。

二、LRU算法机制

LRU算法的全称叫做Least Recently Used,也就是最近最少使用原则来进行数据的淘汰算法。其原理就是,会将数据放入到一个链表中,当链表中的某个元素被访问时,这个元素就被会提到链表的前面,其他元素,默认向后移动;当这个时候我们想缓存中新增一个元素时,也会被增加到链表的头部,那么尾部的最后一个元素就被淘汰了。

lru的实现思想就是:就是刚被访问的数据,在接下来的时间里,更容易被再次访问,而一段时间没有被访问的数据,之后也不会再次访问。但是要将redis的全部数据都放入这样一个链表中的话,redis的数据被频繁访问时,需要不停的移动链表的位置,降低redis的性能,所以redis对LRU算法进行了优化。

在redis中,会给每个数据记录一个最近访问的时间戳(记录在RedisObject的lru字段中),当需要进行数据淘汰时,redis就随机筛选出N个数据放入到候选集合中去,然后比较这N个数据中的lru的值,最小的就会被淘汰。当再次需要淘汰数据时,这个时候筛选数据放入到第一次创建的淘汰集合中,那么这次筛选就不在是随机筛选,而是能进入候选集合的数据的 lru 字段值必须小于候选集合中最小的 lru 值,然后再次将最小的lru的值的数据进行淘汰。其中N的配置项为:

maxmemory-samples 100 # 表示N为100

三、LFU算法机制

LFU(Least frequently used)称为最近使用最少的数据将被淘汰,redis在就是在LRU的基础上增加一个次数统计。其步骤就是根据数据的访问次数进行筛选,淘汰访问次数少的数据,如果访问次数相同,在根据访问时间进行比较,淘汰访问时间久远的数据。redis中的实现方式:就是在RedisObject的字段lru上,拆分为两个部分

  • ldt值:lru字段的前16bit位,还是用来表示时间戳。
  • counter值:lru字段的后8bit位,用来表示数据的访问次数。

当 LFU 策略筛选数据时,Redis 会在候选集合中,根据数据 lru 字段的后 8bit 选择访问次数最少的数据进行淘汰。当访问次数相同时,再根据 lru 字段的前 16bit 值大小,选择访问时间最久远的数据进行淘汰。但是8个bit位,最大只能记录255的值,但是redis中的数据,有时候会被访问成千上万次,那么这个问题如何进行解决呢?

redis对计数进行了优化,并不是数据被访问一次,counter就会被加1,而是遵循如下规则:

  • 当数据被访问一次时,首先用计数器当前的值乘以配置项lfu_log_factor再加1,再取倒数得到一个p值
  • 然后把这个p值和一个取值范围在(0,1)的一个随机数r,进行比大小,只有p值大于r时,counter的值才会被加一
#redis部分源码展示
double r = (double)rand()/RAND_MAX;
...
double p = 1.0/(baseval*server.lfu_log_factor+1);
if (r < p) counter++; 

其中 baseval是计数器的当前值。计数器的默认初始值为5(由代码中的 LFU_INIT_VAL 常量设置),并不是为0,这样可以避免数据刚进入缓存,就因为访问次数少而被立即淘汰。

当lfu_log_factor取不同的值时,实际访问次数下,counter的值的变化情况:

lfu_log_factor

100 hits

1K hits

100K hits

1M hits

10M hits

0

104

255

255

255

255

1

18

49

255

255

255

10

10

18

142

255

255

100

8

11

49

143

255

在实际的使用场景中,还会有这样一种情况,某些数据可能一开始会被大量的访问,但是过了时间段后,就不会再被访问,如果这个时候counter的值很大,就算后续不会被访问,也就不会被redis进行数据淘汰。针对这种情况,在redis中,设计了counter的衰减策略。其实现就是根据lfu_decay_time的配置值,来控制访问次数的衰减。其流程如下:

  • lfu会计算当前时间和数据最近一次访问的时间差值,并将这个差值换算为分钟单位。
  • 然后在将这个差值除以lfu_decay_time值,得到的就是我们需要减去的值
  • 然后再讲counter的值减去这个值

这样就可以保证在一段时间后,可以淘汰这部分数据。

四、redis的淘汰策略怎么选

存在即合理,每种淘汰策略都会有自己的使用场景,我们在设置redis的淘汰策略的时候,就需要结合自己的业务场景去定制化的配置。下面就是我给的一些建议:

  • 可以优先将淘汰策略设置为allkeys-lru,这样可以充分利用lru算法的特性,把最近访问的数据都留在缓存中,长时间没有访问的数据给淘汰掉。
  • 如果业务中的访问数据,没有冷热数据之分,数据的访问时间相差不大,可以采用allkeys-lru策略。
  • 如果在业务中,有需要置顶数据,可以不给置顶数据设置过期时间,然后采用volatile-lru策略来实现。
  • 如果在业务中,有一些定时任务的数据,过了这个时间段后,基本就不会访问的数据,可以采用allkeys-lfu算法。

 

 

 

 

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值