突破Java面试(22) - Redis过时策略及手写LRU

0 欢迎关注 Github

欢迎关注全是干货的技术公众号

1 面试题

Redis的过时策略都有哪些?内存淘汰机制都有哪些?手写一下LRU代码实现?java

2 考点分析

1)往redis里写的数据怎么没了?

生产环境的redis怎么常常会丢掉一些数据?写进去了,过一下子可能就没了。git

问这个问题就说明redis你就没用对啊。redis是缓存,你给当存储了是吧?github

啥叫缓存?用内存当缓存。内存是无限的吗,内存是很宝贵并且是有限的,而磁盘是廉价且大量的!
可能一台机器就几十个G的内存,可是能够有几个T的硬盘空间。
redis主要是基于内存来进行高性能、高并发的读写操做的。面试

那既然内存是有限的,好比redis就只能用10个G,你要是往里面写了20个G的数据,会咋办?固然会干掉10个G的数据,而后就保留10个G的数据了。那干掉哪些数据?保留哪些数据?固然是干掉不经常使用的数据,保留经常使用的数据了。redis

因此说,这是缓存的一个最基本的概念,数据是会过时的,要么是你本身设置个过时时间,要么是redis本身给干掉。算法

set key value 过时时间(1小时)

set进去的key,1小时以后就没了,就失效了segmentfault

2)数据明明都过时了,怎么还占用着内存?

还有一种就是若是你设置好了一个过时时间,你知道redis是怎么给你弄成过时的吗?何时删除掉?若是你不知道,以前有我的就问了,为啥好多数据明明应该过时了,结果发现redis内存占用仍是很高?那是由于你不知道redis是怎么删除那些过时key的。缓存

redis 内存一共是10g,你如今往里面写了5g的数据,结果这些数据明明你都设置了过时时间,要求这些数据1小时以后都会过时,结果1小时以后,你回来一看,redis机器,怎么内存占用仍是50%呢?5g数据过时了,我从redis里查,是查不到了,结果过时的数据还占用着redis的内存。数据结构

若是你连这个问题都不知道,上来就懵了,回答不出来,那线上你写代码的时候,想固然的认为写进redis的数据就必定会存在,后面致使系统各类漏洞和bug,谁来负责?并发

3 详解

3.1 设置过时时间

咱们set key的时候,均可以给一个expire time,就是过时时间,指定这个key好比说只能存活1个小时?10分钟?这个颇有用,咱们本身能够指定缓存到期就失效。

若是假设你设置一个一批key只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?

答案是:按期删除+惰性删除

按期删除

指的是redis默认是每隔100ms就随机抽取一些设置了过时时间的key,检查其是否过时,若是过时就删除。

假设redis里放了10万个key,都设置了过时时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过时key上了。

注意,这里可不是每隔100ms就遍历全部的设置过时时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。

可是问题是,按期删除可能会致使不少过时key到了时间并无被删除掉,那咋整呢?
因此就是

惰性删除

这就是说,在你获取某个key的时候,redis会检查一下 ,这个key若是设置了过时时间那么是否过时了?若是过时了此时就会删除,不会给你返回任何东西。

并非key到时间就被删除掉,而是你查询这个key的时候,redis再懒惰的检查一下

经过上述两种手段结合起来,保证过时的key必定会被干掉。

很简单,就是说,你的过时key,靠按期删除没有被删除掉,还停留在内存里,占用着你的内存呢,除非你的系统去查一下那个key,才会被redis给删除掉。

但实际上这仍是有问题的,若是按期删除漏掉了不少过时key,而后你也没及时去查,也就没走惰性删除,此时会怎么样?大量过时key堆积在内存里,致使redis内存块耗尽了,咋整?

答案是:走内存淘汰机制。

3.2 内存淘汰

若是redis的内存占用过多的时候,此时会进行内存淘汰,有以下一些策略:

redis 10个key,如今已经满了,redis须要删除掉5个key

1个key,最近1分钟被查询了100次
1个key,最近10分钟被查询了50次
1个key,最近1个小时倍查询了1次

  • noeviction:当内存不足以容纳新写入数据时,新写入操做会报错,这个通常没人用吧,(太恶心了,可是默认的)
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(最经常使用)
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个通常没人用吧,为啥要随机,确定是把最近最少使用的key给干掉
  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,移除最近最少使用的key(不太合适)
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,随机移除某个key
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,有更早过时时间的key优先移除

3.3 手写一个LRU

确实有时会问这个,由于有些候选人若是确实过五关斩六将,前面的问题都答的很好,那么其实让他写一下LRU算法,能够考察一下编码功底

你能够现场手写最原始的LRU算法,那个代码量太大了,不太现实

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    
private final int CACHE_SIZE;

    // 这里就是传递进来最多能缓存多少数据
    public LRUCache(int cacheSize) {
        super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); // 这块就是设置一个hashmap的初始大小,同时最后一个true指的是让linkedhashmap按照访问顺序来进行排序,最近访问的放在头,最老访问的就在尾
        CACHE_SIZE = cacheSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > CACHE_SIZE; // 这个意思就是说当map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据
    }

}

最起码你也得写出来上面那种代码,不求本身纯手工从底层开始打造出本身的LRU,可是起码知道如何利用已有的jdk数据结构实现一个java版的LRU

参考

《Java工程师面试突击第1季-中华石杉老师》

本文由博客一文多发平台 OpenWrite 发布!
相关文章
相关标签/搜索