1. 删除策略

1.1. 定时删除 (自动过期)

单单依靠使用设置的过期时间,到期立即删除。可以理解为立即删除策略

 Redis 不可能时时刻刻遍历所有被设置了生存时间的 key,来检测数据是否已经到达过期时间,然后对它进行删除。立即删除能保证内存中数据的最大新鲜度,因为它保证过期键值会在过期后马上被删除,其所占用的内存也会随之释放。但是立即删除对 cpu 是最不友好的。因为删除操作会占用 cpu 的时间,如果刚好碰上了 cpu 很忙的时候,比如正在做交集或排序等计算的时候,就会给 cpu 造成额外的压力,让 CPU 心累,时时需要删除,忙死。。。。。。这会产生大量的性能消耗,同时也会影响数据的读取操作。

总结:对 CPU 不友好,用处理器性能换取存储空间(拿时间换空间)

1.2. 惰性删除 (被动检查)

 数据到达过期时间,不做处理。等下次访问该数据时,如果未过期,返回数据; 发现已过期,删除,返回不存在。 惰性删除策略的缺点是,它对内存是最不友好的。 如果一个键已经过期,而这个键又仍然保留在数据库中,那么只要这个过期键不被删除,它所占用的内存就不会释放。在使用惰性删除策略时,如果数据库中有非常多的过期键,而这些过期键又恰好没有被访问到的话,那么它们也许永远也不会被删除 (除非用户手动执行 FLUSHDB),我们甚至可以将这种情况看作是一种内存泄漏–无用的垃圾数据占用了大量的内存,而服务器却不会自己去释放它们,这对于运行状态非常依赖于内存的 Redis 服务器来说,肯定不是一个好消息

总结:对 memory 不友好,用存储空间换取处理器性能(拿空间换时间)

1.3. 定期删除 (轮询随机)

定期删除策略是前两种策略的折中: 定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。周期性轮询 redis 库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度 特点 1:CPU 性能占用设置有峰值,检测频度可自定义设置 特点 2:内存压力不是很大,长期占用内存的冷数据会被持续清理总结: 周期性抽查存储空间(随机抽查,重点抽查) 举例:redis 默认每个 100ms 检查,是否有过期的 key,有过期 key 则删除。注意:redis 不是每隔 100ms 将所有的 key 检查一次而是随机抽取进行检查(如果每隔 100ms,全部 key 进行检查,redis 直接进去 ICU)。因此,如果只采用定期删除策略,会导致很多 key 到时间没有删除。定期删除策略的难点是确定删除操作执行的时长和频率:如果删除操作执行得太频繁,或者执行的时间太长,定期删除策略就会退化成定时删除策略,以至于将 CPU 时间过多地消耗在删除过期键上面。如果删除操作执行得太少,或者执行的时间太短,定期删除策略又会和惰性删除束略一样,出现浪费内存的情况。因此,如果采用定期删除策略的话,服务器必须根据情况,合理地设置删除操作的执行时长和执行频率。

定期抽样 key,判断是否过期 依旧有漏网之鱼

1.4. 总有漏网之鱼

1 定期删除时,从来没有被抽查到
2 惰性删除时,也从来没有被点中使用过     
上述 2 步骤======> 大量过期的 key 堆积在内存中,导致 redis 内存空间紧张或者很快耗尽必须要有一个更好的兜底方案……

2. 淘汰策略

Redis 支持 8 种不同策略来选择要删除的 key:

  1. noeviction: 不淘汰任何 key,但是内存满时不允许写入新数据默认就是这种策略
  2. volatile-ttl: 对设置了 TTL 的 key,比较 key 的剩余 TTL 值,TTL 越小越先被淘汰
  3. allkeys-random:对全体 key ,随机进行淘汰。也就是直接从 db->dict 中随机挑选
  4. volatile-random:对设置了 TTL 的 key ,随机进行淘汰。也就是从 db->expires 中随机挑选。
  5. allkeys-lru: 对全体 key,基于 LRU 算法进行淘汰
  6. volatile-lru: 对设置了 TTL 的 key,基于 LRU 算法进行淘汰
  7. allkeys-lfu: 对全体 key,基于 LFU 算法进行淘汰
  8. volatile-lfu: 对设置了 TTL 的 key,基于 LFI 算法进行淘汰

2.1. 设置方法

config set maxmemory-policy allkeys-lru

image.png

3. LRU 和 LFU

image.png

比较容易混淆的有两个:

  • LRU(Least Recently Used),最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
  • LFU(Least Frequently Used),最少频率使用。会统计每个 key 的访问频率,值越小淘汰优先级越高。

4. Redis 的内存淘汰算法和原理是什么

Redis 里面的内存淘汰策略,是指内存的使用率达到 maxmemory 上限的时候的一种内存释放的行为。 Redis 里面提供了很多种内存淘汰算法,归纳起来主要就四种

  1. Random 算法,随机移除某个 key
  2. TTL 算法,在设置了过期时间的键中,把更早过期时间的 key 有限移除
  3. LRU 算法,移除最近很少使用的 key
  4. LFU 算法,移除最近很少使用的 key

LRU 是比较常见的一种内存淘汰算法,在 Redis 里面会维护一个大小为 16 的侯选池,这个侯选池里面的数据会根据时间进行排序,然后每一次随机取出 5 个 key 放入到这个侯选池里面,当侯选池满了以后,访问的时间间隔最大的 key 就会从侯选池里面取出来淘汰掉。通过这样的一个设计,就可以把真实的最少访问的 key 从内存中淘汰掉。但是这样的一种 LRU 算法还是存在一个问题,假如一个 key 的访问频率很低,但是最近一次偶尔被访问到,那么 LRU 就会认为这是一个热点 Key,不会被淘汰。

image.png

所以在 Redis4 里面,增加了一个 LFU 的算法,相比于 LRU,LFU 增加了访问频率这个维度来统计数据的热点情况。 (如图)LFU 的主要设计是,使用了两个双向链表形成一个二维双向链表,一个用来保存访问频率,另一个用来保存访问频率相同的所有元素。

当添加元素的时候,访问次数默认为 1,于是找到相同访问频次的节点,然后添加到相同频率节点对应的双向链表头部。当元素被访问的时候,就会增加对应 key 的访问频次,并且把当前访问的节点移动到下一个频次节点。有可能出现某个数据前期访问次数很多,然后后续就一直不用了,如果单纯按照访问频率,这个 key 就很难被淘汰,所以在 LFU 中通过使用频率和上次访问时间来标记数据的热度,如果有有读写,就增加访问频率,如果一段时间内没有读写,就减少访问频率。

image.png

5. 实战经验

image.png

6. 参考与感谢

6.1. 尚硅谷 - 周阳

https://www.bilibili.com/video/BV13R4y1v7sP?p=145&spm_id_from=pageDriver&vd_source=c5b2d0d7bc377c0c35dbc251d95cf204