redis默认使用惰性删除+定期删除。(其中定期删除是每隔 100ms就随机抽取一些设置了过期时间的key)
上述步骤都过堂了,还有漏洞吗?
在配置文件redis.conf 中,可以通过参数 maxmemory 来设定最大内存。
如果不设定该参数默认是无限制的,但是通常会设定其为物理内存的四分之三。
上面总结(2*4得8)
import java.util.LinkedHashMap;
public class LRUCache {
private LinkedHashMap<Integer, Integer> cache;
public LRUCache(int capacity) {
cache = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true){
private static final long serialVersionUID = 1L;
/**
* 此方法为钩子方法,map插入元素时会调用此方法
* 此方法返回true则证明删除最老的因子
*
* 实现LRU的关键方法,如果map里面的元素个数大于了缓存最大容量,则删除链表的顶端元素
* @param eldest
* @return
*/
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<Integer, Integer> eldest){
return size() > capacity;
}
};
}
public int get(int key) {
return cache.getOrDefault(key, -1);
}
public void put(int key, int value) {
cache.put(key, value);
}
@Override
public String toString() {
return cache.toString();
}
}
//双向链表的节点对象
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) {
super();
this.key = key;
this.value = value;
}
}
------------------- ------------------- ------------------- -------------------
//新的插入头部,旧的从尾部移除
//有两个属性链表的头和尾,通过头或者尾就能找到这个链表的其他成员(prev,next)
//
class DoublyLinkedList<K, V>{
Node<K, V> head;
Node<K, V> tail;
public DoublyLinkedList() {
//头尾哨兵节点
this.head = new Node<K, V>();
this.tail = new Node<K, V>();
this.head.next = this.tail;
this.tail.prev = this.head;
}
//node
//star-aa-bb-cc-end
public void addHead(Node<K, V> node) {
node.next = this.head.next;
node.prev = this.head;
this.head.next.prev = node;
this.head.next = node;
}
public void removeNode(Node<K, V> node) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = null;
node.next = null;
}
public Node<K, V> getLast() {
if(this.tail.prev == this.head)
return null;
return this.tail.prev;
}
}
class LRUCache2{
private int cacheSize;//缓存的大小
private Map<Integer, Node<Integer, Integer>> map;//map负责保存节点
private DoublyLinkedList<Integer, Integer> doublyLinkedList;//链表负责节点顺序、删除节点等
//redis确定缓存大小的有参构造方法
public LRUCache2(int cacheSize) {
this.cacheSize = cacheSize;
map = new HashMap<>();
doublyLinkedList = new DoublyLinkedList<>();
}
//redis缓存的获取get方法
public int get(int key) {
if(!map.containsKey(key)) {
return -1;
}
Node<Integer, Integer> node = map.get(key);
//更新节点位置,将节点移置链表头
doublyLinkedList.removeNode(node);
doublyLinkedList.addHead(node);
return node.value;
}
//redis缓存的新增put方法
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);
doublyLinkedList.removeNode(node);
doublyLinkedList.addHead(node);
}else {
if(map.size() == cacheSize) {//已达到最大容量了,把旧的移除,让新的进来
Node<Integer, Integer> lastNode = doublyLinkedList.getLast();
map.remove(lastNode.key);//node.key主要用处,反向连接map
doublyLinkedList.removeNode(lastNode);
}
Node<Integer, Integer> newNode = new Node<>(key, value);
map.put(key, newNode);
doublyLinkedList.addHead(newNode);
}
}
}
save(key, value):首先在 HashMap 找到 Key 对应的节点,如果节点存在,更新节点的值,并把这个节点移动队头。如果不存在,需要构造新的节点,并且尝试把节点塞到队头,如果LRU空间不足,则通过 tail 淘汰掉队尾的节点,同时在 HashMap 中移除 Key。
get(key):通过 HashMap 找到 LRU 链表节点,把节点插入到队头,返回缓存的值。
上面展示了,预设大小是 3 的,LRU存储的在存储和访问过程中的变化。
为了简化图复杂度,图中没有展示 HashMap部分的变化,仅仅演示了上图 LRU 双向链表的变化。
我们对这个LRU缓存的操作序列如下:
save(“key1”, 7);
save(“key2”, 0);
save(“key3”, 1);
save(“key4”, 2);
get(“key2”);
save(“key5”, 3);
get(“key2”);
save(“key6”, 4);