9、认识redis key的过期和内存淘汰

Redis Key的过期与删除

在使用 Redis 做缓存之类的非持久化存储时,我们一般会给 Key 设置一个过期时间,在 Key 到期之后,Redis 就会把这个 Key 自动删除掉,我们之后就再也拿不到这个 KV 数据了。

那 Redis 是如何将过期 Key 清理掉的呢?常见的过期 Key 清理方式(也被称为“过期策略”)有三种:定时过期惰性过期以及定期过期,我们简单介绍一下三者的核心区别以及 Redis 采用的策略。

  • 定时过期策略:该策略需要为每个 Key 关联一个定时器(或是一个全局定时器)记录 Key 的过期时间,当 Key 到期时由定时器触发过期事件,触发执行 Key 的清理逻辑。定时过期策略可以立刻清理过期 Key,释放内存,但是需要额外维护定时器这种复杂的结构。
  • 惰性过期策略:该策略是在客户端访问一个 Key 的时候,判断目标 Key 是否已经到期,如果到期了,就会将其删除,并且返回给客户端 Key 不存在。除此之外的其他时间不会主动去清理 Key。惰性过期策略实现比较简单,不会占用单独的 CPU 时间去执行 Key 过期的操作,而是平摊到了每次 Key 的访问中,但是,如果客户端长时间不访问已经过期的 Key,这些 Key 就会一直存在于内存中,无法被删除,浪费内存空间。
  • 定期过期策略:该策略是前面两种策略的折中方案,定期过期策略会周期性地执行过期 Key 扫描逻辑,并将扫描到的过期 Key 清理掉。使用该策略时,我们可以调整每次扫码的耗时上限、每次扫描 Key 的总量以及两次扫描的时间间隔,保证扫描逻辑不会影响正常的业务逻辑,也可以保证内存不被过期 Key 填满。

在 Redis 中并没有采用定时过期策略,而是选择了惰性过期策略定期过期策略的组合,本节我们就来介绍 Redis 中这两种策略的实现。
在这里插入图片描述

补充图中备注

惰性删除策略: 当一个数据的过期时间到了以后,并不会立即删除数据,而是等到再有请求来读写这个数据时,对数据进行检查,如果发现数据已经过期了,再删除这个数据;

注意点: 在主从同步时,如果从库是3.2以前的版本,那么在服务读请求的过程中,并不会判断数据是否过期,而是直接返回过期数据;

在3.2版本以后,如果读取的数据已经过期了,从库不会删除,会直接返回空值,避免客户端读到过期数据。

定期删除策略: Redis 每隔一段时间(默认 100ms),就会随机选出一定数量的数据,检查它们是否过期,并把其中过期的数据删除,这样就可以及时释放一些内存

Redis内存淘汰机制

Redis 最常见的应用场景就是缓存,我们在使用缓存的时候,一般不会存储 DB 里面全量的数据,而只用于缓存一部分 DB 热点数据,对于非热点数据,需要进行定期删除,防止 Redis 内存被撑爆,也就是我们常说“内存淘汰”机制。

edis.conf 中的 maxmemory 配置项,它指定了 Redis 的最大内存,单位是 byte,当 Redis 内存占用达到这个值的时候,Redis 就会开始按照指定的策略淘汰 Key,从而达到释放内存的效果。

下面是 Redis 支持的内存淘汰策略。

  • no-enviction 策略:该策略不会触发内存淘汰,但是当客户端再次发送新建 Key 的请求或是修改已有 Key 的请求(例如,SET、LPUSH 等)时,Redis 会直接返回错误。对 DEL 命令以及 GET 等查询命令是可以正常响应的。

  • volatile-lru 策略:Redis 会按照近似 LRU 的算法,从设置了过期时间的 Key 中,选取 Key 进行淘汰。注意,Redis 使用的 LRU 算法只是近似 LRU,其底层原理是:采样一部分 Key,然后从被采样的 Key 集合中淘汰最久没有被访问的 Key,这样可以避免遍历全部有过期时间的 Key 集合。

    在 Redis 3.0 中,近似 LRU 算法进行了一次升级,引入了一个维护淘汰 Key 的候选池,这样可以提高筛选 Key 的精准度,使得近似 LRU 算法的效果更加接近于真正的 LRU 算法。

    另外,我们可以通过参数来调整近似 LRU 算法每次采样的 Key 的个数,从而调整近似 LRU 算法的精准度。

  • allkeys-lru 策略:筛选 Key 的算法依旧是近似 LRU 算法,但是筛选 Key 的范围不再限于设置了过期时间的 Key,而是扩展到 Redis 内全部的 Key。

  • volatile-random 策略:从设置了过期时间的 Key 中随机选择 Key 进行淘汰。

  • allkeys-random 策略:从全部 Key 中随机选择 Key 进行淘汰

  • volatile-ttl 策略:从设置了过期时间的 Key 中筛选将要过期的 Key 进行淘汰。注意,这也是一个通过抽样实现的近似算法。

  • volatile-lfu 策略:使用近似 LFU 算法从设置了过期时间的 Key 中,筛选出最近访问频率最低的 Key 进行淘汰

  • allkeys-lfu 策略:使用近似 LFU 算法从全部 Key 中,筛选出最近访问频率最低的 Key 进行淘汰。

上面介绍的这些淘汰策略,都是可以通过 redis.conf 文件中的 maxmemory-policy 配置项进行指定的

这里简单区分一下 LRU 和 LFU 两种算法。

  • LRU 的全称是 Least Recently Used,含义是最近最少使用,核心思想是如果一个 Key 在最近一段时间内没有被用到,那么将来被使用到的可能性也很小,所以应该被优先从内存中淘汰。
  • LFU 的全称是 Least Frequently Used,含义是最近访问频率最低,核心思想是如果一个 Key 在最近一段时间内的访问频率都很低,那么这个 Key 应该被优先从内存中淘汰。

LRU和LFU算法
在这里插入图片描述

补充图中备注

LRU算法-具体实现原理: Redis 中的 LRU 策略,会在每个数据对应的 RedisObject 结构体中设置一个 lru 字段,用来记录数据的访问时间戳。
在进行数据淘汰时,LRU 策略会在候选数据集中淘汰掉 lru 字段值最小的数据(也就是访问时间最久的数据)

LFU算法-具体实现原理: Redis 在实现 LFU 策略的时候,只是把原来 24bit 大小的 lru 字段,又进一步拆分成了两部分。

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

当 LFU 策略筛选数据时,Redis 会在候选集合中,根据数据 lru 字段的后 8bit 选择访问次数最少的数据进行淘汰。当访问次数相同时,再根据 lru 字段的前 16bit 值大小,选择访问时间最久远的数据进行淘汰。

count计数规则: 每当数据被访问一次时,首先,用计数器当前的值乘以配置项 lfulogfactor 再加 1,再取其倒数,得到一个 p 值;然后,把这个 p 值和一个取值范围在(0,1)间的随机数 r 值比大小,只有 p 值大于 r 值时,计数器才加 1

lfu_decay_time(控制访问次数的衰减)-备注: LFU 策略会计算当前时间和数据最近一次访问时间的差值,并把这个差值换算成以分钟为单位。然后,LFU 策略再把这个差值除以 lfudecaytime 值,所得的结果就是数据 counter 要衰减的值。

示例:假设 lfudecaytime 取值为 1,如果数据在 N 分钟内没有被访问,那么它的访问次数就要减 N

内存淘汰策略

在这里插入图片描述

相关参数与使用建议
在这里插入图片描述

补充图中备注

Redis 选出的数据个数 N: 当需要淘汰数据时,Redis 需要挑选数据进入第一次淘汰时创建的候选集合。这儿的挑选标准是:能进入候选集合的数据的 lru 字段值必须小于候选集合中最小的 lru 值。当有新数据进入候选数据集后,如果候选数据集中的数据个数达到了 maxmemory-samples,Redis 就把候选数据集中 lru 字段值最小的数据淘汰出去

redis架构整理图: https://www.processon.com/v/67f8740b6bf6c24a4a209ba3?cid=67f6a1daffb7dc3cd65e4e95

个人公众号: 行云代码

参考文章

redis架构整理图: https://www.processon.com/v/67f8740b6bf6c24a4a209ba3?cid=67f6a1daffb7dc3cd65e4e95

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uncleqiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值