失效策略:缓存淘汰策略有哪些?
Posted 陈汤姆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了失效策略:缓存淘汰策略有哪些?相关的知识,希望对你有一定的参考价值。
最近看了点LRU的文章,所以自己写了一篇作为自己的知识总结!如有雷同告知侵删
说到失效策略就要知道失效的是什么以及通过什么方式失效?
本文涉及知识比较浅,能让你知道什么是LRU以及类似的共享策略,最后用代码具体实现了LRU策略。
首先我们知道在计算机中内存是有限的,这就导致了我们存储数据时会出现内存过载的情况,那么此时就需要通过一定的策略将在内存中的数据剔除一部分,空出的部分储存新数据,这就用到了失效策略。
对于失效策略我们平时也会接触,比如
1、FIFO(First In First Out):先进先出策略,这个就是队列中最长被记住的,先进入的数据先被淘汰。
2、LFU(Least Frequently Used):最少被使用策略,这个策略在于统计每个数据的使用次数,最少被使用的数据会被淘汰。
3、LRU(Least Recently Used):最近最少使用策略,该策略淘汰的是最近一段时间最少被使用的数据。
这三种策略也是最经典的失效策略,大部分缓存应用模型都是基于这些策略来实现的。
以Redis为例进行分析缓存淘汰策略,当Redis节点分配的内存达到最大值以后,按理来说不会再存储数据,但是Redis为了继续提供服务就会启动内存淘汰策略,将一些数据清楚继续提供缓存服务。
Redis的配置文件中也标明了各种淘汰策略的概念
noeviction,这是默认的策略,对于写请求会拒绝服务,直接返回错误,这种策略下可以保证数据不丢失;
allkeys-lru,这种策略操作的范围是所有 key,使用 LRU 算法进行缓存淘汰;
volatile-lru,这种策略操作的范围是设置了过期时间的 key,使用 LRU 算法进行淘汰;
allkeys-random,这种策略下操作的范围是所有 key,会进行随机淘汰数据;
volatile-random,这种策略操作的范围是设置了过期时间的 key,会进行随机淘汰;
volatile-ttl,这种策略操作的范围是设置了过期时间的 key,根据 key 的过期时间进行淘汰,越早过期的越优先被淘汰。
通过对Redis中的这些策略可以知道,Redis通过重新设计LRU算法实现了缓存淘汰机制,同时也衍生出了自己设定的淘汰策略,保证Redis服务的正常运行。
对于LRU算法,根据最近最少使用这个关键字进行分析,首先考虑移动数据的问题,需要选择一个数据结构方便移动数据,这样的结构我想通过列表来实现,毕竟只要通过移动指针来交换位置就可以了,时间的复杂度为O1,并且考虑到如果我们要交换节点,这就需要用到双向链表了,直接通过前驱与后驱指针操作,方便快捷。
而对于获取数据而言,这里我想到的就算是hashMap,直接通过key获取就可以,考虑到这里大体的结构已经清晰了,通过hamap跟list绑定,结构可以是HashMap<Integer,ListNode>。
结构有了接下来就需要思路实现具体过程了,先说下我的想法;
首先需要设定缓存大小,因为后面通过这个缓存大小来判断是否达到了最大容量,如果是那么就移除最近最少使用的数据。
第二点就是何时移除,这里从填充数据以及获取数据的操作考虑,毫无疑问肯定是填充数据时移除,填充数据时做个判断就好了,判断是否达到了缓存最大值,如果是,那么执行移除操作。
第三点就是移动操作,移动操作时需要考虑将最近被使用的数据移动到头节点,这里考虑先删后添加到头节点的方式
有了以上思路接下来就是代码,Talk is cheap,show me the code。
class LRUCache {
class Node {
int key;
int val;
Node next;
Node prev;
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
Map<Integer, Node> map;
private Node head;
private Node tail;
private int size;//初始容量
private int currentSize;//当前容量
public LRUCache(int capacity) {
size = capacity;
map = new HashMap<>();
}
public int get(int key) {
Node node = map.get(key);
if(node != null) {
int res = node.val;
removeNode(node);
moveTohead(node);
return res;
}
return -1;
}
public void put(int key, int value) {
// 判断有没有这个 key
Node node = map.get(key);
if(node != null) {
node.val = value;
// 先移除这个节点,再把这个node放在最前面去
removeNode(node);
moveTohead(node);
} else {
node = new Node(key, value);
if(currentSize < size) {
moveTohead(node);
map.put(key, node);
currentSize++;
} else {
// 踢走老的
map.remove(tail.key);
removeNode(tail);
moveTohead(node);
map.put(key, node);
}
}
}
private void moveTohead(Node node) {
//判断链表是否为空,不为空则操作指针将该节点放到头节点
if(head == null) {
head = tail = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
}
private void removeNode(Node node) {
// 该方法借鉴别人的代码,自己比较好理解的一种方法
if(head == tail) {
head = tail = null;
} else {
if(head == node) {
head = head.next;
node.next = null;
} else if (tail == node) {
tail = tail.prev;
tail.next = null;
node.prev = null;
} else {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = null;
node.next = null;
}
}
}
}
(该代码对应leetcode146题)
以上就是对LRU缓存失效做的一部分总结,当然了也通过Redis缓存解释了在Redis中存在的缓存失效策略。本文并没有从计算机内存方面做具体的研究分析,有兴趣可以做深入学习。
我是chenTom,bye了个bye!
以上是关于失效策略:缓存淘汰策略有哪些?的主要内容,如果未能解决你的问题,请参考以下文章