Redis缓存淘汰之LRU

我们知道Redis是基于内存来存储数据的,这也是它之所以查询速度这么快的原因之一。但是在数据量较大的场景下如果一直网缓存中存数据那么就会报OOM异常,因此我们就需要使用下面八种缓存淘汰策略对缓存进行删除处理。
1、noeviction:返回错误当内存限制达到,并且客户端尝试执行会让更多内存被使用的命令。
2、allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
3、volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存 放。
4、allkeys-random: 回收随机的键使得新添加的数据有空间存放。
5、volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
6、volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间 存放。
7、allkeys-lfu:对所有key使用LFU算法进行删除
8、volatile-lfu:对所有设置了过期时间的key使用LFU算法进行删除

本文主要针对LRU算法展开论述
一、什么是LRU算法

就是一种缓存淘汰策略。
计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置。但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留在缓存里,方便之后继续使用。那么,什么样的数据,我们判定为「有用的」的数据呢?
LRU 缓存淘汰算法就是一种常用策略。LRU 的全称是 Least Recently Used,也就是说我们认为最近使用过的数据应该是是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。
通俗点讲就删除缓存时优先删除最久未被操作的数据,增删改查都属于操作的范畴。

二、LRU 算法描述
LRU 算法实际上是让你设计数据结构:首先要接收一个 capacity 参数作为缓存的最大容量,然后实现两个 API,一个是 put(key, val) 方法存入键值对,另一个是 get(key) 方法获取 key 对应的 val,如果 key 不存在则返回 -1。

三、LRU 算法设计
分析上面的操作过程,要让 put 和 get 方法的时间复杂度为 O(1),我们可以总结出 cache 这个数据结构必要的条件:查找快,插入快,删除快,有顺序之分。
因为显然 cache 必须有顺序之分,以区分最近使用的和久未使用的数据;而且我们要在 cache 中查找键是否已存在;如果容量满了要删除最后一个数据;每次访问还要把数据插入到队头。
那么,什么数据结构同时符合上述条件呢?哈希表查找快,但是数据无固定顺序;链表有顺序之分,插入删除快,但是查找慢。所以结合一下,形成一种新的数据结构:哈希链表。

四、代码实现
很多编程语言都有内置的哈希链表或者类似 LRU 功能的库函数,但是为了帮大家理解算法的细节,我们用 Java 自己造轮子实现一遍 LRU 算法。

class LRUCache {
    Map<Integer,Node> map;
    Node head;
    Node tail;
    int capacity;
    public LRUCache(int capacity) {
        this.map=new HashMap<>(capacity);
        this.capacity=capacity;
        this.head=new Node(0,0);
        this.tail=new Node(0,0);
        this.head.next=tail;
        this.tail.pre=head;
    }
    public int get(int key) {
        if(!map.containsKey(key)){
            return -1;
        }
        Node node=map.get(key);
        move2first(node);
        return node.val;
    }
    public void put(int key, int value) {
        if(map.containsKey(key)){
            Node node=map.get(key);
            node.val=value;
            map.put(key,node);
            move2first(node);
            return;
        }
        if(map.size()==capacity){
            Node last=this.tail.pre;
            map.remove(last.key);
            removeNode(last);
        }
        Node node=new Node(key,value);
        add2first(node);
        map.put(key,node);
    }
    public void removeNode(Node node){//删除节点
        node.pre.next=node.next;
        node.next.pre=node.pre;
        node.next=null;
        node.pre=null;
    }
    public void add2first(Node node){//添加节点
        Node curr=this.head.next;
        this.head.next=node;
        node.next=curr;
        node.pre=this.head;
        curr.pre=node;
    }
    public void move2first(Node node){//移动节点
        node.pre.next=node.next;
        node.next.pre=node.pre;
        add2first(node);
    }
    //定义链表
    class Node{
        Node pre;//上一个节点
        Node next;//下一个节点
        int key;
        int val;
        public Node(int key,int val){
            this.key=key;
            this.val=val;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值