Redis缓存满了,如何存放数据?缓存淘汰策略
我们的redis使用的是内存空间来存储数据的,但是内存空间毕竟有限,随着我们存储数据的不断增长,当超过了我们的内存大小时,即在redis中设置的缓存大小(maxmeory 4GB),redis会怎么处理呢?
Redis 当中提供了不同的淘汰策略来处理这种场景。
首先 Redis 提供了一个参数 maxmemory 来配置 Redis 最大使用内存:
maxmemory <bytes>
或者也可以通过命令 config set maxmemory 1GB 来动态修改。
如果没有设置该参数,那么在 32 位的操作系统中 Redis 最多使用 3GB 内存,而在 64 位的操作系统中则不作限制。
Redis 中提供了 8 种淘汰策略,可以通过参数 maxmemory-policy 进行配置:
根据是否进行数据淘汰可以分为:不淘汰的数据策略和7种淘汰数据策略。
PS:淘汰策略也可以直接使用命令 config set maxmemory-policy <策略> 来进行动态配置。
LRU 算法
LRU 全称为:Least Recently Used。即:最近最少使用原则来进行数据的淘汰算法。这个主要针对的是使用时间。
Redis 改进后的 LRU 算法
在 Redis 当中,并没有采用传统的 LRU 算法,因为传统的 LRU 算法存在 2 个问题:
- 需要额外的空间进行存储。
- 可能存在某些 key 值使用很频繁,但是最近没被使用,从而被 LRU 算法删除。
为了避免以上 2 个问题,Redis 当中对传统的 LRU 算法进行了改造,通过抽样的方式进行删除。
配置文件中提供了一个属性 maxmemory_samples 5,默认值就是 5,表示随机抽取 5 个 key 值,然后对这 5 个 key 值按照 LRU 算法进行删除,所以很明显,key 值越大,删除的准确度越高。
LRU 算法原理:
将数据放入到一个链表中,当链表中的某个元素被访问时,这个元素就被会提到链表的前面,其他元素,默认向后移动;
当这个时候我们想缓存中新增一个元素时,也会被增加到链表的头部,那么尾部的最后一个元素就被淘汰了。
LFU 算法
LFU 全称为:Least Frequently Used。即:最近使用最少的数据将被淘汰。这个主要针对的是使用频率。
当我们采用 LFU 回收策略时,lru 属性的高 16 位用来记录访问时间(last decrement time:ldt,单位为分钟),低 8 位用来记录访问频率(logistic counter:logc),简称 counter。
LFU 算法步骤
根据数据的访问次数进行筛选,淘汰访问次数少的数据,如果访问次数相同,在根据访问时间进行比较,淘汰访问时间久远的数据。
redis中的实现方式:
就是在RedisObject的字段lru上,拆分为两个部分
访问频次递增
LFU 计数器每个键只有 8 位,它能表示的最大值是 255,所以 Redis 使用的是一种基于概率的对数器来实现 counter 的递增。
给定一个旧的访问频次,当一个键被访问时,counter 按以下方式递增:
- 提取 0 和 1 之间的随机数 R。
- counter - 初始值(默认为 5),得到一个基础差值,如果这个差值小于 0,则直接取 0,为了方便计算,把这个差值记为 baseval。
- 概率 P 计算公式为:1/(baseval * lfu_log_factor + 1)。
- 如果 R < P 时,频次进行递增(counter++)。
公式中的 lfu_log_factor 称之为对数因子,默认是 10 ,可以通过参数来进行控制:
lfu_log_factor 10
访问频次递减
如果访问频次 counter 只是一直在递增,那么迟早会全部都到 255,也就是说 counter 一直递增不能完全反应一个 key 的热度的,所以当某一个 key 一段时间不被访问之后,counter 也需要对应减少。
counter 的减少速度由参数 lfu-decay-time 进行控制,默认是 1,单位是分钟。默认值 1 表示:N 分钟内没有访问,counter 就要减 N。
lfu-decay-time 1
计算公式:
取出当前的时间戳和对象中的 lru 属性进行对比,计算出当前多久没有被访问到,比如计算得到的结果是 100 分钟没有被访问,然后再去除配置参数 lfu_decay_time,如果这个配置默认为 1也即是 100/1=100,代表 100 分钟没访问,所以 counter 就减少 100。
redis的淘汰策略怎么选?
每种淘汰策略都会有自己的使用场景,我们在设置redis的淘汰策略的时候,就需要结合自己的业务场景去定制化的配置。
-
可以优先将淘汰策略设置为allkeys-lru,这样可以充分利用lru算法的特性,把最近访问的数据都留在缓存中,长时间没有访问的数据给淘汰掉。
-
如果业务中的访问数据,没有冷热数据之分,数据的访问时间相差不大,可以采用allkeys-lru策略。
-
如果在业务中,有需要置顶数据,可以不给置顶数据设置过期时间,然后采用volatile-lru策略来实现。
-
如果在业务中,有一些定时任务的数据,过了这个时间段后,基本就不会访问的数据,可以采用allkeys-lfu算法。