Redis:缓存被我写满了,该怎么办?


Redis:缓存被我写满了,该怎么办?

文章插图
 
介绍
Redis:缓存被我写满了,该怎么办?

文章插图
 
redis是一个内存数据库,当Redis使用的内存超过物理内存的限制后,内存数据会和磁盘产生频繁的交换,交换会导致Redis性能急剧下降 。所以在生产环境中我们通过配置参数maxmemoey来限制使用的内存大小 。
当实际使用的内存超过maxmemoey后,Redis提供了如下几种可选策略 。
noeviction:写请求返回错误
volatile-lru:使用lru算法删除设置了过期时间的键值对 volatile-lfu:使用lfu算法删除设置了过期时间的键值对 volatile-random:在设置了过期时间的键值对中随机进行删除 volatile-ttl:根据过期时间的先后进行删除,越早过期的越先被删除
allkeys-lru:在所有键值对中,使用lru算法进行删除 allkeys-lfu:在所有键值对中,使用lfu算法进行删除 allkeys-random:所有键值对中随机删除
我们来详细了解一下lru和lfu算法,这是2个常见的缓存淘汰算法 。「因为计算机缓存的容量是有限的,所以我们要删除那些没用的数据,而这两种算法的区别就是判定没用的纬度不一样 。」
LRU算法「lru(Least recently used,最近最少使用)算法,即最近访问的数据,后续很大概率还会被访问到,即是有用的 。而长时间未被访问的数据,应该被淘汰」
lru算法中数据会被放到一个链表中,链表的头节点为最近被访问的数据,链表的尾节点为长时间没有被访问的数据
Redis:缓存被我写满了,该怎么办?

文章插图
 
「lru算法的核心实现就是哈希表加双向链表」 。链表可以用来维护访问元素的顺序,而hash表可以帮我们在O(1)时间复杂度下访问到元素 。
「至于为什么是双向链表呢」?主要是要删除元素,所以要获取前继节点 。数据结构图示如下
Redis:缓存被我写满了,该怎么办?

文章插图
 
使用双向链表+HashMap双向链表节点定义如下
public class ListNode<K, V> {    K key;    V value;    ListNode pre;    ListNode next;    public ListNode() {}    public ListNode(K key, V value) {        this.key = key;        this.value = value;    }}封装双向链表的常用操作
public class DoubleList {    private ListNode head;    private ListNode tail;    public DoubleList() {        head = new ListNode();        tail = new ListNode();        head.next = tail;        tail.pre = head;    }    public void remove(ListNode node) {        node.pre.next = node.next;        node.next.pre = node.pre;    }    public void addLast(ListNode node) {        node.pre = tail.pre;        tail.pre = node;        node.pre.next = node;        node.next = tail;    }    public ListNode removeFirst() {        if (head.next == tail) {            return null;        }        ListNode first = head.next;        remove(first);        return first;    }}封装一个缓存类,提供最基本的get和put方法 。「需要注意,这两种基本的方法都涉及到对两种数据结构的修改」 。
public class MyLruCache<K, V> {    private int capacity;    private DoubleList doubleList;    private Map<K, ListNode> map;    public MyLruCache(int capacity) {        this.capacity = capacity;        map = new HashMap<>();        doubleList = new DoubleList();    }    public V get(Object key) {        ListNode<K, V> node = map.get(key);        if (node == null) {            return null;        }        // 先删除该节点,再接到尾部        doubleList.remove(node);        doubleList.addLast(node);        return node.value;    }    public void put(K key, V value) {        // 直接调用这边的get方法,如果存在,它会在get内部被移动到尾巴,不用再移动一遍,直接修改值即可        if ((get(key)) != null) {            map.get(key).value = value;            return;        }        // 如果超出容量,把头去掉        if (map.size() == capacity) {            ListNode listNode = doubleList.removeFirst();            map.remove(listNode.key);        }        // 若不存在,new一个出来        ListNode node = new ListNode(key, value);        map.put(key, node);        doubleList.addLast(node);    }}


推荐阅读