一:Redis最大占用内存
打开redis的redis.conf配置文件,设置maxmemory参数,注意maxmemory是byte字节类型。
如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位
操作系统下最多使用3GB内存。一般推荐Redis设置内存为最大物理内存的的四分之三。
如何修改reids内存设置?
1.手动进入redis.conf配置文件修改
2.通过命令修改
查看redis内存使用情况命令:info memory
二:Redis内存使用超出了设置的最大值会怎样?
我们可以故意将redis内存设置一个字节模拟一下
然后进入redis随便设置一个值就会报OOM
所以为了不报OOM,才会有内存淘汰策略去杜绝内存打满。
三:内存淘汰策略
首先,我们有必要了解一下redis的过期策略。
在Redis中过期的key不会立刻从内存中删除,而是会同时以下面两种策略进行删除:
惰性删除:当key被访问时检查该key的过期时间,若已过期则删除;已过期未被访问的数据仍保持在内存中,消耗内存资源;
定期删除:每隔一段时间,随机检查设置了过期的key并删除已过期的key;维护定时器消耗CPU资源;
以上两种的过期策略都有各自的缺陷,所以redis提供了内存淘汰策略来进行补充。
8中策略:
1. noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键
2. allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
3. volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
4. allkeys-random:加入键的时候如果过限,从所有key随机删除
5. volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
6. volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
7. volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
8. allkeys-lfu:从所有键中驱逐使用频率最少的键
redis默认使用的noeviction,推荐使用allkeys-lru
四:LRU
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法。
LRU的算法核心是哈希链表(hashmap + DoubleLinkedList),查找用哈希,增删用链表,能很好的满足查找增删都是o(1)。
利用LinkedHashMap手写LRU算法:
public class LRUDemo extends LinkedHashMap {
private int capacity;
public LRUDemo(int capacity) {
//accessOrder参数要设置为true,不然不会将最近使用顶到后面去
super(capacity,0.75F,true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Entry eldest) {
return super.size() > capacity;
}
public static void main(String[] args) {
LRUDemo lruDemo = new LRUDemo(3);
lruDemo.put(1,"a");
lruDemo.put(2,"b");
lruDemo.put(3,"c");
System.out.println(lruDemo.keySet());
lruDemo.put(4,"d");
System.out.println(lruDemo.keySet());
lruDemo.get(3);
System.out.println(lruDemo.keySet());
lruDemo.get(3);
System.out.println(lruDemo.keySet());
lruDemo.get(2);
System.out.println(lruDemo.keySet());
}
}
打印结果:
从结果可以看出,当容器已满的时候再向容器新增数据时,会删除掉排在第一个位置的数据,且当一个数据被最近使用时,会将这个数据排到末尾(这与accessOrder参数设置为true有关)
完全手写LRU算法:
// map 负责查找,构建一个虚拟的双向链表,它里面安装的是一个个的node节点,作为数据载体
public class LRUCacheDemo {
//1.构建一个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;
}
}
//2.构建一个虚拟的双向列表,里面放的就是我们的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;
}
}
private int cacheSize;
Map<Object,Node<Object,Object>> map;
DoubleLinkedList<Object,Object> doubleLinkedList;
public LRUCacheDemo(int cacheSize) {
this.cacheSize = cacheSize;
map = new HashMap<>();
doubleLinkedList = new DoubleLinkedList<>();
}
public Object get(Object key) {
if (!map.containsKey(key)) {
return -1;
}
Node<Object,Object> node = map.get(key);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
return node.value;
}
public void put(Object key, Object value) {
// 已经存在这个key,执行更新操作
if (map.containsKey(key)) {
Node<Object, Object> node = map.get(key);
node.value = value;
map.put(key,node);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
} else {
// 容器满了
if (map.size() == cacheSize) {
Node lastNode = doubleLinkedList.getLastNode();
map.remove(lastNode.key);
doubleLinkedList.removeNode(lastNode);
}
// 新增
Node<Object, Object> newNode = new Node<>(key, value);
map.put(key,newNode);
doubleLinkedList.addHead(newNode);
}
}
public static void main(String[] args) {
LRUCacheDemo lruCacheDemo = new LRUCacheDemo(3);
lruCacheDemo.put(8,1);
lruCacheDemo.put(6,2);
lruCacheDemo.put(7,3);
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put(4,4);
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put(6,5);
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put(5,5);
System.out.println(lruCacheDemo.map.keySet());
lruCacheDemo.put("a",5);
System.out.println(lruCacheDemo.map.keySet());
}
}