redis经典面试问题(五)

347 阅读3分钟

常见面试问题

  • reids同一时间有大量的key同时过期可能会发生什么现象,该如何处理?

  • redis的过期策略有哪些?

  • 讲讲redis的内存淘汰机制?

  • 手写LRU代码实现?

redis的过期策略(定期删除+惰性删除)

当redis同一时间有大量的key同时过期的话,可能会使redis造成短暂的卡顿现象,所以我们在程序中设置key的过期时间,尽量不要设定固定值,可以带上一个随机值。当我们对redis的key设置了过期时间,redis是怎么删除这些key的呢?答案是定期删除+惰性删除。所谓定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样肯定是不合适的。实际上redis是每隔100ms随机抽取一些key来检查和删除的。那这里有一个问题,由于定期删除是随机抽取key的,那没删除的key怎么办呢?那redis采用了惰性删除这样一种策略,也就是说,在你获取某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。

redis的内存淘汰机制

其实上面的答案实际可能还是有问题的,如果有大量的过期key,通过定期和惰性删除都没有被干掉,一直占用的redis的内存,那这个时候怎么办呢?答案是我们接下来要聊的内存淘汰机制,当redis内存占用过多时,会走内存淘汰机制,主要有以下几种策略:

①noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错

②allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(最常用的

③allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key

④volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key

⑤volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key

⑥volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除

LRU算法

LRU就是Least recently used,最近最少使用,如果空间不足淘汰掉最近最少使用的数据,这里提供一种比较简单的实现方式,就是使用java的LinkedHashMap,LinkedHashMap继承HashMap,增加了head和tail指针,用于实现双向链表。它是通过维护一个运行于所有条目的双向链表来保证了元素迭代的顺序,该迭代顺序可以是插入顺序或者是访问顺序。这里我们不具体展开聊LinkedHashMap了,直接参考如下代码实现LRU算法:

public class Test {
    public static void main(String[] args) {
        LruMap<String, String> linkedHashMap =
                new LruMap<String, String>(4, 0.75f, true);
        linkedHashMap.put("111", "111");
        linkedHashMap.put("222", "222");
        linkedHashMap.put("333", "333");
        linkedHashMap.put("444", "444");
        for(Map.Entry<String,String> m:linkedHashMap.entrySet()){
            System.out.println(m.getKey()+":"+m.getValue());
        }
        linkedHashMap.get("111");
        System.out.println("************");
        for(Map.Entry<String,String> m:linkedHashMap.entrySet()){
            System.out.println(m.getKey()+":"+m.getValue());
        }
        linkedHashMap.put("555", "555");
        System.out.println("************");
        for(Map.Entry<String,String> m:linkedHashMap.entrySet()){
            System.out.println(m.getKey()+":"+m.getValue());
        }
    }
}
class  LruMap<K,V> extends LinkedHashMap<K,V>{
    private int size;
    public LruMap(int initialCapacity,
                        float loadFactor,
                        boolean accessOrder) {
        super(initialCapacity, loadFactor, accessOrder);
        this.size = initialCapacity;
    }

    /**
     * LinkedHashMap默认是不会自动删除链表头节点数据的,我们需要覆盖这个方法
     * @param eldest
     * @return
     */
    @Override
    protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
        if(size() > size){
            return true;
        }
        return false;
    }
}