关于Redis LRU算法原理及实现方式

173 阅读2分钟

本文正在参加「技术专题19期 漫谈数据库技术」活动

LRU

选择最近最久未使用的数据予以淘汰。(一种常用的页面置换算法)


谈到LRU我们就不得不说一下Redis的八大淘汰策略

noeviction:不会驱逐任何的key allkeys-lru:对所有的key使用LRU算法进行删除 volatile-lru:对所有设置了过期时间的key使用LRU算法进行删除 allkeys-random:对所有key随机删除 volatile-random:对所有设置了过期时间的key随机删除 volatile-ttl:删除马上要过期的key allkeys-lfu:对所有key使用LFU算法进行删除 volatile-lfu:对所有设置了过期时间的key使用LFU算法进行删除


Redis的八大策略用于对Redis本身进行内存优化的策略,通过这一系列的策略可以更好地让Redis存储更多的有用数据,而其中LRU算法更是其中两种策略的核心


核心 关于LRU算法的核心是基于哈希链表(哈希表+双向链表的结合体,时间复杂度O(1)) 本质:HashMap+DoubleLinkedList 在这里插入图片描述 在这里插入图片描述 而当我们在面试的时候有时也会被问到是否能够手写LRU算法,所以基于此,我给出下面两种方式


第一种:参考LinkedHashMap(带有结构顺序的Map),LRU算法的天生实现者

在这里插入图片描述 测试结果

在这里插入图片描述 在这里插入图片描述


第二种:不依赖JDK纯手写

public class LRUDemo {    
    //缓存的坑位    
    private int cacheSize;    
    Map<Integer,Node<Integer,Integer>> map;    
    DoubleLinkedList<Integer,Integer> doubleLinkedList;    
    //map负责查找,构建一个虚拟的双向链表,里面安装的就是一个个节点Node,作为数据载体    
    class Node<K,V>    
    {        
        K key;        
        V value;        
        Node<K,V> prev;        
        Node<K,V> next;        
        public Node(){            
            this.prev = this.next = null;        
        }        
        public Node(K key,V value){            
            this.key = key;            
            this.value = value;            
            this.prev = this.next = null;        
        }    
    }    
    //构造一个虚拟的双向链表,里面安放Node    
    class DoubleLinkedList<K,V>    {        
        Node<K,V> head;        //尾节点        
        Node<K,V> tail;        
        public DoubleLinkedList(){            
            head = new Node<>();            
            tail = new Node<>();            
            head.next = tail;            
            tail.prev = head;        
        }        
        //添加到头节点        
        public void addHead(Node<K,V> node){            
            node.next = head.next;            
            node.prev = head;            
            head.next.prev = node;            
            head.next = node;        
        }        
        //删除节点        
        public void removeNode(Node<K,V> node){            
            node.next.prev = node.prev;            
            node.prev.next = node.next;            
            node.prev = null;            
            node.next = null;        
        }        
        //获得最后一个节点        
        public Node getLastNode(){            
            return tail.prev;        
        }    
    }    
    public int get(int key){        
        if(!map.containsKey(key)){            
            return -1;        
        }        
        Node<Integer, Integer> node = map.get(key);
        doubleLinkedList.removeNode(node);        
        doubleLinkedList.addHead(node);        
        return node.value;    
    }    
    public void put(int key,int value){        
        if(map.containsKey(key)){            
            //相当于更新操作            
            Node<Integer, Integer> node = map.get(key);            
            node.value = value;            
            map.put(key,node);            
            doubleLinkedList.removeNode(node);            
            doubleLinkedList.addHead(node);        
        }else{            
            //坑位满            
            if(map.size() == cacheSize){                
                Node<Integer,Integer> lastNode = doubleLinkedList.getLastNode();
                map.remove(lastNode.key);
                doubleLinkedList.removeNode(lastNode);            
            }            
            Node<Integer, Integer> node = new Node<>(key, value);
            map.put(key,node);            
            doubleLinkedList.addHead(node);        
        }    
    }
}

这里就不展示第二种的测试结果了(因为我懒┭┮﹏┭┮),代码应该是没什么问题,感兴趣的话可以尝试自己写一写(●ˇ∀ˇ●)


最后的补充 如何设置Redis的八大策略呢

第一种:命令式

config set maxmemory-policy 策略名

第二种:配置文件 在这里插入图片描述 差不多就到这儿吧,如果有什么补充或者疑问,可以留言哈🤭