Redis内存设置
1.配置文件配置
可以通过在Redis安装目录下的redis.conf文件设置内存大小。
// 设置Redis可占用最大内存
maxmemory 50mb
2.Redis命令设置
127.0.0.1:6379> config set maxmemory 50mb
Redis内存回收
如下图,通过查看Redis的配置文件redis.conf可知,当Redis内存使用完时,redis提供了几种内存回收策略(我使用的版本是3.2.8):
volatile-lru -> 根据LRU算法删除设置了超时属性(expire)的key,直到腾出足够空间为止。如果没有key可以淘汰,回退到noeviction策略;
allkeys-lru -> 从所有key中使用LRU算法进行淘汰,直到腾出足够空间为止;
volatile-random -> 从设置了超时属性的key中随机淘汰数据,直到腾出足够空间为止。如果没有key可以淘汰,回退到noeviction策略;
allkeys-random -> 从所有key中随机淘汰数据,直到腾出足够空间为止;
volatile-ttl -> 从设置了超时属性的key中,根据过期时间进行淘汰,越早过期的key越优先被淘汰。如果没有key可以淘汰,回退到noeviction策略;
noeviction(默认策略) -> 拒绝所有写入操作并直接返回错误(DEL请求和部分特殊请求除外)。
设置内存淘汰策略
redis内存淘汰策略默认是noeviction。
先获取当前内存淘汰策略:
127.0.0.1:6379> config get maxmemory-policy
1.修改redis.conf配置文件:
maxmemory-policy allkeys-lru
2.redis命令修改
127.0.0.1:6379>config set maxmemory-policy allkeys-lru
LRU算法
LRU(Least Recently Used),内存管理的一种缓存置换算法,对于在使用内存作为缓存时,如果缓存被数据占满并且还要添加数据,需要淘汰一部分老数据以释放内存空间。
使用java代码实现一个简单的LRU算法:
public class LRUCache<k, v> {
/**
* 容量
*/
private int capacity;
/**
* 统计当前节点数
*/
private int count;
/**
* 缓存节点
*/
private Map<k, Node<k, v>> nodeMap;
/**
* 头节点
*/
private Node<k, v> head;
/**
* 尾节点
*/
private Node<k, v> tail;
public LRUCache() {
}
public LRUCache(int capacity) {
if (capacity < 1) {
throw new IllegalArgumentException(String.valueOf(capacity));
}
this.capacity = capacity;
this.nodeMap = new HashMap<>();
// 初始化头节点和尾节点,
// 利用哨兵模式减少判断头节点和尾节点为空
Node headNode = new Node(null, null);
Node tailNode = new Node(null, null);
headNode.next = tailNode;
tailNode.pre = headNode;
this.head = headNode;
this.tail = tailNode;
}
public void put(k key, v value) {
Node<k, v> node = nodeMap.get(key);
if (node == null) {
// 如果容量已满
if (count >= capacity) {
// 先移除一个节点
removeNode();
}
node = new Node<>(key, value);
// 添加节点
addNode(node);
} else {
// 移动节点到头节点
moveNodeToHead(node);
}
}
public Node<k, v> get(k key) {
Node<k, v> node = nodeMap.get(key);
if (node != null) {
moveNodeToHead(node);
}
return node;
}
private void removeNode() {
Node node = tail.pre;
// 从链表里面移除
removeFromList(node);
// 从缓存中删除
nodeMap.remove(node.key);
count--;
}
private void removeFromList(Node<k, v> node) {
Node pre = node.pre;
Node next = node.next;
pre.next = next;
next.pre = pre;
node.next = null;
node.pre = null;
}
private void addNode(Node<k, v> node) {
// 添加节点到链表头部
addToHead(node);
// 并添加到缓存中
nodeMap.put(node.key, node);
count++;
}
private void addToHead(Node<k, v> node) {
Node next = head.next;
next.pre = node;
node.next = next;
node.pre = head;
head.next = node;
}
public void moveNodeToHead(Node<k, v> node) {
// 从链表里面移除
removeFromList(node);
// 添加节点到头部
addToHead(node);
}
class Node<k, v> {
k key;
v value;
Node pre;
Node next;
public Node(k key, v value) {
this.key = key;
this.value = value;
}
}
}
我从网上查阅资料得知,Redis使用的是近似LRU算法。所谓近似,是因为Redis通过随机采样的方式去淘汰数据,每次随机选取5(默认)个key,从里面淘汰最近最少使用的key(可以通过修改参数maxmemory-samples设置随机选取数量)。
Redis给每个key增加了一个24bit的字段,用来存储key最后一次被访问的时间。
而Redis3.0对近似LRU算法进行了优化,新算法维护了一个key的候选池,其大小为16,池中的数据根据时间排序,第一次选取的key都会放入池中,之后每次取到的key只有在其访问时间小于池中最小的时间才会入池。池满后,如果有新的key要入池,则将池中访问时间最大(最近被访问)的key移除。
LFU算法
LFU(Least Frequently Used)算法是Redis4.0以上版本中添加的一种淘汰策略。根据key最近被访问的频率进行淘汰,访问较少的优先被淘汰。
LFU算法避免了LRU中将偶尔被访问一次的key作为热点数据。
LFU有两种策略:
volatile-lfu -> 根据LFU算法删除设置了超时属性(expire)的key,直到腾出足够空间为止。如果没有key可以淘汰,回退到noeviction策略;
allkeys-lfu -> 从所有key中使用LFU算法进行淘汰,直到腾出足够空间为止。