😄作者简介: 小曾同学.com,一个致力于测试开发的博主⛽️,主要职责:测试开发、CI/CD
如果文章知识点有错误的地方,还请大家指正,让我们一起学习,一起进步。
😊 座右铭:不想当开发的测试,不是一个好测试✌️。
如果感觉博主的文章还不错的话,还请点赞、收藏哦!👍
前言 ❤️
redis是基于内存存储,所以性能比较强,但是单节点的 Redis 内存大小不宜过大,要不然会影响持久化或者主从同步性能,可以在redis.conf配置文件中设置最大内存,maxmemory 1gb
,但是当内存使用达到上限时,就无法存储更多数据了,这时该怎么办呢?
在Redis中提供了两种内存回收策略:
- 过期策略
-
在过期策略中会了解到以下内容:
(1)Rediskey 的 TTL 记录方式:在RedisDB中通过一个 Dict 记录每个 Key 的 TTL 时间
(2)过期 key 的删除策略:
惰性清理:每次查找 key 时判断是否过期,如果过期则删除 定期清理:定期抽样部分 key,判断是否过期,如果过期则删除。
(3)定期清理的两种模式:
SLOW 模式执行频率默认为10,每次不超过25ms FAST 模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
- 淘汰策略
会了解到八种淘汰策略
过期策略🌟
通过expire
命令给 Redis 的 key 配置TTL(存活时间)
127.0.0.1:6379> set num jenny
OK
127.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算法进行淘汰
要是用 LRU
和LFU
算法的话需要知道 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需要放进去,然后把淘汰池的最小的挤出去,然后升序排列。
如果觉的不错的话,别忘记三连哦!