官:我看你简历提到xxx项目使用了redis

小弱鸡:嗯,因为xxxx的性能问题,经过排查之后,发现性能瓶颈在数据库上面,所以引入了redis

面试官:行,那你了解redis的过期策略吗?

小弱鸡:有了解过,因为redis是基于内存来进行高性能、高并发的读写操作的,既然是内存,那肯定有空间的限制,如果只有10g内存,一直往里面写数据,那肯定不行,所以采用一些过期策略把不需要的数据删除、或者是淘汰掉。

面试官:那都有哪些过期策略?

小弱鸡:我了解的有 定期删除、惰性删除两种

面试官:你先讲讲定期删除怎么实现?

小弱鸡好像有点兴奋:所谓定期删除,指的是 redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。

面试官:为什么是随机抽取?

小弱鸡:假如在redis 里插入10w个key,并且都设置了过期时间,如果每次都检查所有key,那cpu基本上都消耗在过期key的检查上了,redis对外的性能也会大大降低,简直就是一场灾难。

面试官:随机检查会存在什么问题?

小弱鸡:可能导致本已经过期的key没有被扫描到,而继续留在内存中,并占用空间,等待被删除。

面试官:这种情况怎么解决?

小弱鸡又兴奋了:这时候就需要第二种过期策略了,惰性删除,就是在获取某个 key 的时候,redis 会检查一下 ,如果这个 key 设置了过期时间,并且已经过期了,那么就直接删除,返回空。

面试官面带一丝笑意:嗯,那再考虑一种情况,如果大量的key没有被扫描到,且已过期,也没有被再次访问,即没有走惰性删除,这些大量过期 key 堆积在内存里,导致 redis 内存块耗尽了,这种情况下,怎么办?

小菜鸡想了会,抓了抓脑袋:redis内部提供了内存淘汰机制,应该有好几种策略,但我只知道LRU算法。

面试官:嗯,那你手写一个LRU算法?

小菜鸡*花一紧,这不是给自己挖坑么!!! 如果从头开始写一个完整的LRU算法,那会要了命,幸好小菜鸡还记得 ​​LinkedHashMap​​​,可以基于 ​​LinkedHashMap​​实现一个简单版本的LRU算法。


  1. ​class LRUCache<K, V> extends LinkedHashMap<K, V> {​
  2. ​ private final int CACHE_SIZE;​

  3. ​ * @param cacheSize 缓存大小​
  4. ​ */​
  5. ​ // true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。​
  6. ​ public LRUCache(int cacheSize) {​
  7. ​ super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);​
  8. ​ CACHE_SIZE = cacheSize;​
  9. ​ }​

  10. ​ @Override​
  11. ​ // 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。​
  12. ​ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {​

  13. ​ return size() > CACHE_SIZE;​
  14. ​ }​
  15. ​}​

小菜鸡写完之后,轻轻舒了一口气。



(1)volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰。

(2)volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

(3)volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

(4)allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

(5)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

(6)no-enviction:禁止淘汰数据