写点什么

Redis 是如何做内存回收的

  • 2024-06-04
    上海
  • 本文字数:2304 字

    阅读完需:约 8 分钟

Redis是如何做内存回收的

前言

redis 是基于内存存储,所以性能比较强,但是单节点的 Redis 内存大小不宜过大,要不然会影响持久化或者主从同步性能,可以在 redis.conf 配置文件中设置最大内存,maxmemory 1gb,但是当内存使用达到上限时,就无法存储更多数据了,这时该怎么办呢?

在 Redis 中提供了两种内存回收策略:

  1. 过期策略

在过期策略中会了解到以下内容:

(1)Rediskey 的 TTL 记录方式:

在 RedisDB 中通过一个 Dict 记录每个 Key 的 TTL 时间

(2)过期 key 的删除策略:

  • 惰性清理:每次查找 key 时判断是否过期,如果过期则删除

  • 定期清理:定期抽样部分 key,判断是否过期,如果过期则删除。

(3)定期清理的两种模式:

  • SLOW 模式执行频率默认为 10,每次不超过 25ms

    FAST 模式执行频率不固定,但两次间隔不低于 2ms,每次耗时不超过 1ms

  1. 淘汰策略

会了解到八种淘汰策略

过期策略

通过expire命令给 Redis 的 key 配置 TTL(存活时间)

127.0.0.1:6379> set num jennyOK127.0.0.1:6379> expire num 10 # 设置10s(integer) 1 127.0.0.1:6379> get num # 在10s内key存在"jenny"127.0.0.1:6379> get num # 当key的TTL到期以后,再次访问则返回的是nil,说明这个key不存在了(nil)
复制代码

当 TTL 到期之后,对应的内存得到释放,从而实现内存回收的目的。那么 Redis 是如何知道某个 key 是否过期的呢?

这就涉及到 Redis 数据库的存储原理,Redis 是基于 key-value 存储的,因此所有的 key、value 都会保存在一个 Dict 结构中,也可称为 keyspace,如果一个 key 设置了 TTL,则会另外存储在一个 Dict 中,也就是存 key 和它对应的 TTL 存活时间。也就是说,在 Redis 数据库结构体中,有两个 Dict:一个用来记录 key-value ,另一个用来记录 key-TTL。这样直接查看 key 对应的 TTL 即可知道 key 是否到期。


那么,当 TTL 到期后就立即删除了 key 吗?

不会立即删除,可以采用惰性删除和定期删除。

  • 惰性删除,就是当在访问一个 key 的时候去检查 key 的存活时间,如果存活时间已经过期了才会执行删除。但是这里又有一个问题,如果这个 key 一直没有人访问,那么就一直不会被删除,这样也会占用内存,所以为了解决这个问题,Redis 中提供了一个周期删除的过期策略,就是通过一个定时任务

  • 定期删除:Redis 中提供了一个周期删除的过期策略,就是通过一个定时任务,周期性的抽样部分过期的 key,然后执行删除,执行周期有两种:

  • Redis 初始化时会设置一个定时任务 serverCron(),按照 server.hz 的频率来清理过期的 key,模式为 SLOW。

  • SLOW 模式规则如下:

  1. 执行频率受 server.hz 影响,默认为 10,即每秒执行 10 次,每个执行周期为 100ms

  2. 执行清理耗时不超过一次执行周期的 25%

  3. 逐个遍历 db,逐个遍历 db 中的 bucket(哈希表的数组),抽取 20 个 key 判断是否过期

  4. 如果(执行总时长)没达到时间上限(25ms)并且过期 key 比例大于 10%,再进行一次抽样,否则结束

  • Redis 的每个事件循环前会调用 beforceSleep() 函数,清理过期的 key,模式为 FAST(执行的频率比较高,执行时间比较短 1ms)

  • FAST 模式规则如下(过期 key 比例小于 10%不执行):

  1. 执行频率受 beforeSleep()调用频率影响,但两次 FAST 模式间隔不低于 2ms

  2. 执行清理耗时不超过 1ms

  3. 逐个遍历 db,逐个遍历 db 中的 bucket,抽取 20 个 key 判断是否过期

  4. 如果没达到时间上限 (1ms)并且过期 key 比例大于 10%,再进行一次抽样,否则结束

内存淘汰策略

内存淘汰就是当 Redis 内存使用达到设置的阈值时,Redis 主动挑选部分 key 删除以释放更多内存。

那么,

  • Redis 什么时候检查内存够不够:当内存被访问的时候就去检查内存。

  • 如何挑选要删除的 key?在 Redis 中提供了八种淘汰策略


(1)noeviction:不淘汰任何 key,但是内存满时不允许写入新数据,默认就是这种策略。

在 redis.conf 中有 maxmemory-policy noeviction 配置

(2)volatile-ttl:对设置了下 TTL 的 key,比较 key 的剩余 TTL 值,TTL 越小越先被淘汰

(3)allkeys-random:对全体 key,随机进行淘汰。也就是直接从 db→dict 中随机挑选

(4)volatile-random:对设置了 TTL 的 key,随机进行淘汰。也就是从 db-sexpires 中随机挑选。

(5)allkeys-lru:对全体 key,基于 LRU 算法进行淘汰(最少最近使用)

(6)volatile-lru:对设置了 TTL 的 key,基于 LRU 算法进行淘汰

(7)allkeys-lfu:对全体 key,基于 LFU 算法进行淘汰(最少频繁使用)

(8)volatile-lfu:对设置了 TTL 的 key,基于 LFI 算法进行淘汰


要是用 LRULFU算法的话需要知道 key 的使用频率和最近一次的使用时间,在 Redis 的数据都会被封装为 RedisObject 结构:【了解即可】

下图是八种淘汰策略的流程图,


在判断内存策略中是采用 LRU 还是 LFU 时需要判断 key 的值的 TTL,但是在 Redis 中有大量的 key,如果将 TTL 进行排序是不现实的,eviction_pool 相当于淘汰池,想淘汰一些 key,但是又不想进行遍历,所以从数据库中挑选一些样本,放入 eviction_pool 中,然后再整体比较一次,看看他们的 TTL 和使用频率,基于这个去做淘汰。redis 中总共有 16 个数据库,从数据库中随机挑选 key,放入 eviction_pool 淘汰池中,但是每种算法的挑选方式不同,所以淘汰方式也不同,但是对于 eviction_pool 淘汰池需要制定一个标准的淘汰标准:按照 key 的某一个值的升序进行排列,值越大的优先淘汰,按什么方法升序排列:LRU:用当前减去最近一次被访问的时间(时间最长越容易被淘汰);LFU:255-频率次数(最越大越容易被淘汰),TTL:用最大时间-剩余时间(值越大越容易被淘汰)计算完成上述方法之后,还需要判断 eviction_pool 淘汰池有没有满,如果满了则需要比较要放入淘汰池的 key 的计算结果比淘汰池的某个值大,这样的 key 需要放进去,然后把淘汰池的最小的挤出去,然后升序排列。

如果觉的不错的话,别忘记三连哦!

发布于: 刚刚阅读数: 5
用户头像

出来乍到的程序猿,有一颗成为IT精英的梦想 2020-08-28 加入

座右铭:不想当开发的测试,不是一个好测试✌️。 我是一个致力于测试开发的博主,主要职责:测试开发、CI/CD,偶尔跟着开发学习音视频知识,希望输出的内容可以帮助和我一样初入职场,经验不足的小白。

评论

发布
暂无评论
Redis是如何做内存回收的_redis_小曾同学.com_InfoQ写作社区