失效策略:缓存淘汰策略有哪些?

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!

以上是关于失效策略:缓存淘汰策略有哪些?的主要内容,如果未能解决你的问题,请参考以下文章

2021-05-14 Redis面试题 Redis的内存淘汰策略有哪些?

Redis篇:持久化淘汰策略,缓存失效策略

Redis篇:持久化淘汰策略,缓存失效策略

缓存的淘汰策略有几种?分别怎么用?

缓存策略的选择

06 | 链表(上):如何实现LRU缓存淘汰算法?