LRU简介
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。 该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间t,当须淘汰一个页面时,选择现有页面中其t 值最大的,即最近最少使用的页面予以淘汰。
Redis中的LRU
当需要从缓存中淘汰数据时,我们希望淘汰那些将来不可能再被使用的数据,保留那些将来还会频繁访问的数据。
一个解决方法就是通过LRU进行预测:最近被频繁访问的数据将来被访问的可能性也越大。缓存中的数据一般会有这样的访问分布:一部分数据拥有绝大部分的访问量。
当访问模式很少改变时,可以记录每个数据的最后一次访问时间,拥有最少空闲时间的数据可以被认为将来最有可能被访问到。
配置参数
Redis配置中和LRU有关的有三个:
- maxmemory:配置Redis存储数据时指定限制的内存大小,比如100m。当缓存消耗的内存超过这个数值时, 将触发数据淘汰。如果配置为0时,表示缓存的数据量没有限制。
- maxmemory_policy:触发数据淘汰后的淘汰策略
- maxmemory_samples: 随机采样的精度,也就是随即取出key的数目。该数值配置越大, 越接近于真实的LRU算法,但是数值越大,相应消耗也变高,对性能有一定影响。
淘汰策略
淘汰策略即maxmemory_policy的赋值有以下几种:
- noeviction:如果缓存数据超过了maxmemory限定值,并且客户端正在执行的命令(大部分的写入指令,但DEL和几个指令例外)会导致内存分配,则向客户端返回错误响应
- allkeys-lru: 对所有的键都采取LRU淘汰
- volatile-lru: 仅对设置了过期时间的键采取LRU淘汰
- allkeys-random: 随机回收所有的键
- volatile-random: 随机回收设置过期时间的键
- volatile-ttl: 仅淘汰设置了过期时间的键---淘汰生存时间TTL(Time To Live)更小的键
链表实现一个简单的LRU
package com.stone.java001;
public class LRUForLinkList {
private LRUNode headNode;
// 容量大小,类比与redis设置的maxmemory大小
private Integer capacity;
// 实际元素长度
private Integer length;
public LRUForLinkList() {
this.headNode = new LRUNode();
this.capacity = 10;
this.length = 0;
}
//插入数据
public void add(Integer data) {
//判断要插入的数据是否存在,如果存在,删除存在的元素,将该元素插入进头部
//如果不存在,判断是否超出容量,如果超出容量,删除最后一个元素,将该元素插入头部
LRUNode preNode = findPreNode(data);
if (preNode != null) {
delNode(preNode);
insertBegin(data);
} else {
if (length >= capacity) {
delEndNode();
}
insertBegin(data);
}
}
//在头部插入元素
public void insertBegin(Integer data) {
this.headNode.setNext(new LRUNode(data, this.headNode.getNext()));
length++;
}
//找到该元素的前一个节点
public LRUNode findPreNode(Integer data) {
LRUNode node = this.headNode;
while (node.getNext() != null) {
if (data.equals(node.getNext().getData())) {
return node;
}
node = node.getNext();
}
return null;
}
//删除指定值的元素
public void delNode(LRUNode preNode) {
LRUNode tmp = preNode.getNext();
preNode.setNext(tmp.getNext());
tmp = null;
length--;
}
//删除末尾节点
public void delEndNode() {
LRUNode ptr = headNode;
if (ptr.getNext() == null) {
return;
}
while (ptr.getNext().getNext() != null) {
ptr = ptr.getNext();
}
LRUNode tmp = ptr.getNext();
ptr.setNext(null);
tmp = null;
length--;
}
//打印该链表
private void printAll() {
LRUNode node = headNode.getNext();
while (node != null) {
System.out.print(node.getData() + ",");
node = node.getNext();
}
System.out.println();
}
//测试。。。
public static void main(String[] args) {
LRUForLinkList lruForLinkList = new LRUForLinkList();
for(int i=0;i<10;i++){
lruForLinkList.add(i);
}
lruForLinkList.printAll();
lruForLinkList.add(8);
lruForLinkList.printAll();
lruForLinkList.add(11);
lruForLinkList.printAll();
}
}
class LRUNode {
private Integer data;
private LRUNode next;
public LRUNode() {
this.next = null;
}
public LRUNode(Integer data) {
this.data = data;
}
public LRUNode(Integer data, LRUNode next) {
this.data = data;
this.next = next;
}
public Integer getData() {
return data;
}
public void setData(Integer data) {
this.data = data;
}
public LRUNode getNext() {
return next;
}
public void setNext(LRUNode next) {
this.next = next;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。