LRU Cache及Redis实现

651 阅读1分钟

LRUCache实现【Java】

import java.util.HashMap;
import java.util.Map;

public class LRUCache<K, V> {

    private Map<K, Node<K,V>> map = new HashMap<>();
    private Node<K,V> head;
    private Node<K,V> tail;
    private int capacity;
    private int pos;

    public LRUCache(int capacity) {
        this.head = new Node<>(null, null, null, null);
        this.tail = new Node<>(null, null, null, null);
        this.head.next = this.tail;
        this.tail.pre = this.head;
        this.capacity = capacity;
    }

    static class Node<K, V> {
        private K key;
        private V val;
        private Node<K,V> pre;
        private Node<K,V> next;

        public Node(K key, V val, Node<K,V> pre, Node<K,V> next) {
            this.key = key;
            this.val = val;
            this.pre = pre;
            this.next = next;
        }
    }

    public V get(K key){
        if(map.containsKey(key)){
            Node<K,V> valNode = map.get(key);
            moveToHead(valNode);
            return valNode.val;
        }
        return null;
    }

    public V put(K key, V value){
        if(map.containsKey(key)){
            Node<K,V> valNode = map.get(key);
            valNode.val = value;
            moveToHead(valNode);
        } else {
            Node<K,V> newNode = new Node<>(key, value, null,null);
            map.put(key, newNode);
            addToHead(newNode);
            drainOutIfNesesary();
        }
        return null;
    }

    public void drainOutIfNesesary(){
        if(pos > capacity){
            map.remove(tail.pre.key);
            tail.pre.pre.next = tail;
            tail.pre = tail.pre.pre;
            pos--;
        }
    }

    public void addToHead(Node<K,V> newNode){
        newNode.next = head.next;
        head.next.pre = newNode;
        head.next = newNode;
        newNode.pre = head;
        pos++;
    }


    public V remove(K key){
        if(map.containsKey(key)){
            Node<K,V> node = map.get(key);
            node.next.pre = node.pre;
            node.pre.next = node.next;
            map.remove(key);
            pos--;
            return node.val;
        }
        return null;
    }

    private void moveToHead(Node<K,V> valNode){
        valNode.next.pre = valNode.pre;
        valNode.pre.next = valNode.next;
        head.next.pre = valNode;
        valNode.next = head.next;
        head.next = valNode;
        valNode.pre = head;
    }


    public static void main(String[] args) {
        LRUCache<String, Integer> cache = new LRUCache<>(3);
        cache.put("a",1);
        cache.put("b",2);
        cache.put("c",3);
        cache.put("d", 4);
        cache.put("e", 5);
        print(cache);
        cache.remove("d");
        print(cache);
        cache.put("f", 6);
        cache.put("g", 7);
        print(cache);
        cache.get("f");
        print(cache);
        cache.put("e", 10);
        print(cache);
    }

    static void print(LRUCache cache){
        for(Node curNode = cache.head.next; !curNode.equals(cache.tail); curNode = curNode.next){
            System.out.print(curNode.val+", ");
        }
        System.out.println();
    }

}

Redis LRU Cache实现

如果按照HashMap和双向链表实现,需要额外的存储存放 next 和 prev 指针,牺牲比较大的存储空间 Redis策略:随机取出若干个key,然后按照访问时间排序后,淘汰掉最不经常使用的 LRU淘汰策略:

  • volatile-lru 设置了过期时间的key参与近似的lru淘汰策略
  • allkeys-lru 所有的key均参与近似的lru淘汰策略 Redis会基于server.maxmemory_samples配置选取固定数目的key,然后比较它们的lru访问时间,然后淘汰最近最久没有访问的key。maxmemory_samples的值越大,Redis的近似LRU算法就越接近于严格LRU算法,但是相应消耗也变高,对性能有一定影响,样本值默认为5。