LRU算法和LFU算法

news2024/12/29 9:38:20

LRU(Least Recently Used)最近最少使用,淘汰最近最少使用的数据,

LFU(Least Frequently Used)最近最不频繁用,淘汰最不常用的数据。

LRU算法

传统的LRU基于链表实现。基本的一个LRU算法实现就是【Hash表+双向链表】。

算法执行:

        按最近使用顺序排序,被操作的数据元素,会拆链表,把该数据元素移到最前面,合链表。

        链表尾部代表最久未使用的数据元素。

操作两个问题:

        ① 链表存储所有数据带来开销;

        ② 大量数据访问时,过多的解链表、合链表会严重影响性能。

LFU算法

核心思想是:默认访问多的数据越重要。

解决偶尔访问一次数据就会存留较长时间的情况,会比较符合应用场景。但实现起来较复杂,基本的一个LFU算法实现需要【两个Hash表+多个双向链表】。不过两者的Hash表都是用来存储key-节点的关系。不同在于LFU需要多一个Hash去记录在哪一个双向链表。

执行:

  1. key不存在返回null;

  2. key存在,返回对应节点的value值。同时,需将节点的访问次数+1,然后将该节点从原双向链表中移除,添加到新的访问次数对应的双向链表中。若hash表不存在对应的双向链表则需要先创建链表。(访问次数与双向链表数对应)

        Redis实现

Redis的内存淘汰策略中,就有LRU和LFU算法的实现,不完全相同但类似。Redis3.0之前使用的内存淘汰策略为:volatile-lru(基于设置过期时间数据进行淘汰的LRU算法)

Redis实现LRU算法:

        是在Redis的对象结构体中添加一额外字段,记录最新一次的访问时间。

当Redis进行内存淘汰时, 会使用随机采样的方式来淘汰数据,淘汰访问时间距离最近的数据。

优点:

        相比传统LRU算法,省内存空间,不必去维护一个较大占空间的链表。

        相比传统LRU算法,省去链表数据移到操作,提高性能。

缺点:

        缓存污染问题:一次读取大量数据后,这些数据会留在数据库较长时间。这也是引入LFU算法

        的目的之一。

Redis实现LFU算法:

        是在Redis对象头中存放 ldt (占16bit)和 logc (占8bit)分别记录key的最新访问时间戳和key的访问频次。 当为什么这里需要时间呢,不是论次数吗?因为这里的 logc 不是记录单纯的访问次数,而是频次,会随时间衰减,随访问次数增加而增长,因此有 ldt 存在的必要性。

ps:Redis中是有两个参数lfu-decay-time和lfu-log-factor分别用于设置logc的衰减和增长速度。

回想起Leetcode上一道经典的算法题LRU(纯思路题,并不涉及复杂算法):

力扣icon-default.png?t=N3I4https://leetcode.cn/problems/lru-cache/附上题解:

class LRUCache {
    class LinkList {
        int key;
        int value;
        LinkList prev;
        LinkList next;
        public LinkList() {}
        public LinkList(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }
    private int size;
    private int capacity;
    private HashMap<Integer, LinkList> map = new HashMap<>();
    private LinkList head;   // 头指针
    private LinkList tail;   // 尾指针

    public LRUCache(int capacity) {
        this.capacity = capacity;
        size = 0;
        head = new LinkList();
        tail = new LinkList();
        head.next = tail;
        tail.prev = head;  // 头尾互指易漏
    }
    
    public int get(int key) {
        LinkList node = map.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.value;
    }
    
    public void put(int key, int value) {
        LinkList node = map.get(key);
        if (node == null) {
            if (size++ == capacity) {
                LinkList tailNode = removeTail();
                map.remove(tailNode.key);
                size--;  // 必须有size--,不然其实一直在size++
            }
            node = new LinkList(key, value);  // 因为下面同一个了,所以这里必须更新node
            addHead(node);
            map.put(key, node);
        } else {
            node.value = value;  // 更新value值
        }
        moveToHead(node);
    }

    void moveToHead(LinkList node) {
        removeNode(node);
        addHead(node);
    }

    void removeNode(LinkList node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    void addHead(LinkList node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    LinkList removeTail() {  
        LinkList tailNode = tail.prev;
        removeNode(tailNode);
        return tailNode;
    }
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/442744.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Opencv 基本操作八 不均匀光照下的图像二值化探讨

在进行图像二值化时总是存在一些明部、暗部的干扰&#xff0c;单一的使用opencv提供的原始二值化方法很难做到预期效果。一般我们都会采用分块二值化&#xff08;将图像切为多个局部进行二值化&#xff09;、对比度提升&#xff08;对值域进行线性或者非线性变换、直方图均衡化…

C#串口通信从入门到精通(2)——串口相关参数介绍

1、端口号&#xff08;Port&#xff09; 我们使用一个串口的时候&#xff0c;首先是要打开这个串口&#xff0c;那么我们怎么知道电脑上现在支持几个串口呢&#xff1f;对应的端口号又是什么呢&#xff1f; 由于我的电脑系统是window11&#xff0c;下面就以window11为例介绍如…

网络请求实战-缓存、缓存清理和HTTP缓存

目录 缓存介绍 清空策略&#xff08;FIFO&#xff09; 实战&#xff1a;fifo的memory函数 实战&#xff1a;LRU算法 HTTP缓存 Cache-Control 强制缓存 协商缓存 协商缓存-2&#xff08;用的最多的&#xff09; 小结 缓存介绍 早期cpu&#xff0c;内存设计上都有缓存…

开发常用的 Linux 命令4(系统、进程和其它)

开发常用的 Linux 命令4&#xff08;系统、进程和其它&#xff09; 作为开发者&#xff0c;Linux是我们必须掌握的操作系统之一。因此&#xff0c;在编写代码和部署应用程序时&#xff0c;熟练使用Linux命令非常重要。这些常用命令不得不会&#xff0c;掌握这些命令&#xff0…

【JUC】volatile和JMM

【JUC】volatile和JMM 文章目录 【JUC】volatile和JMM1. volatile1.1 特点1.2 内存语义 2. 内存屏障2.1 分类2.2 什么叫保证有序性&#xff1f;2.3 内存屏障的4种插入策略 3. volatile特性3.1 保证可见性3.2 volatile读写过程3.3 没有原子性3.4 指令禁重排(有序性) 4. 正确使用…

python标识符概念及规范

在python中 能取名字的东西非常非常多 例如 我们之前学的变量 以及后面要接触的 函数 类&#xff0c;等等&#xff0c;等等 而我们给这些取的名字 被统称为 标识符 而 python中 标识符的命名也是有限制的 主要有三种 1 内容限定 2 大小写铭感 3 不能使用关键字 内容限定来讲…

leetcode6_N字形变换

如有错误&#xff0c;感谢不吝赐教、交流 leetcode6 题目描述 将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “PAYPALISHIRING” 行数为 3 时&#xff0c;排列如下&#xff1a; P A H N A P L S I…

HTB-SecNotes

HTB-SecNotes 信息收集8808端口80端口通过CSRF获取通过二次注入 立足tyler -> administrator 信息收集 8808端口 Windows IIS 10.0 可以从官方文档查看10.0版本可能的操作系统。 80端口 通过CSRF获取 目录扫描发现需要登陆后继续进一步操作啊。 对其进行简单的SQL注入测…

数据库基础篇 《7.单行函数》

目录 1. 函数的理解 1.1 什么是函数 1.2 不同DBMS函数的差异 ​编辑1.3 MySQL的内置函数及分类 ​编辑 2. 数值函数 2.1 基本函数 ​编辑 2.2 角度与弧度互换函数 2.3 三角函数 ​编辑 2.4 指数与对数 ​编辑 2.5 进制间的转换 ​编辑3. 字符串函数 ​编辑…

SAM(segment anything model)分割一切 Demo测试及API调用

SAM 分割一切 一&#xff0c;SAM介绍1.1 介绍1.2 项目链接 二&#xff0c;Demo-Test&#xff1a;2.1 Demo功能介绍2.1.1&#xff0c;首页就是这个SAM&#xff0c;点击try demo&#xff0c;可以选择它的自带图片&#xff0c;也可以自己添加。2.1.2 , 自己上传图片测试&#xff1…

[java基础]面向对象(五)

访问控制修饰符&#xff1a;--------------保护数据的安全(隐藏数据、暴露行为)&#xff0c;实现封装 public&#xff1a;公开的&#xff0c;任何类 private&#xff1a;私有的&#xff0c;本类 protected&#xff1a;受保护的&#xff0c;本类、派生类、同包类 默认的&…

learn_C_deep_3 (最名不符实的关键字 - static、static关键字总结、基本数据类型、最冤枉的关键字 - sizeof)

目录 最名不符实的关键字 - static stati修饰全局变量和函数 static修饰局部变量 static关键字总结 几个问题 1.c语言要设置全局变量和函数可以跨文件使用的原因 2.C程序地址空间是什么样的&#xff1f; 3.局部变量为什么具有临时性 4.全局变量为什么具有全局性 5.为…

vue-cli版本号始终是2.9.6,且无法删除,安装更新无效的问题。

参考博客 目录 1.问题出现原因2.我的解决办法&#xff1a;删除原脚手架&删除原vuevue.cmd 1.问题出现原因 从各种博客我得知&#xff0c;这种问题出现在2处&#xff1a; 没有卸载原来的脚手架原来的vue和vue.cmd没删除干净 2.我的解决办法&#xff1a;删除原脚手架&…

[oeasy]python0135_命名惯用法_name_convention

命名惯用法 回忆上次内容 上次 了解了isidentifier的细节 关于 关键字关于 下划线 如何查询 变量所指向的地址&#xff1f; id 如何查询 已有的各种变量&#xff1f; locals 如果 用一个变量a的值 给另一个变量b 赋值是什么样的过程 呢&#xff1f;&#xff1f;&#x1f914;…

当,Kotlin Flow与Channel相逢

前言 之前的文章已经分析了Flow的相关原理与简单使用&#xff0c;Flow之所以用起来香&#xff0c;Flow便捷的操作符功不可没&#xff0c;而想要熟练使用更复杂的操作符&#xff0c;那么需要厘清Flow和Channel的关系。 本篇文章构成&#xff1a; 1. Flow与Channel 对比 1.1 Fl…

AVL树(C++实现)

文章目录 AVL树的概念AVL树结点定义AVL树的插入AVL树的旋转左单旋右单旋左右单旋右左双旋 AVL树的验证AVL树的性能AVL树及测试完整代码 AVL树的概念 二叉搜索树虽然可以缩短查找的效率,但如果数据有序或接近有序,那么二叉搜索树将退化为单支树,查找元素则相当于在顺序表中搜索…

从零手写Resnet50实战——利用 torch 识别出了虎猫和萨摩耶

大家好啊&#xff0c;我是董董灿。 自从前几天手写了一个慢速卷积之后&#xff08;从零手写Resnet50实战—手写龟速卷积&#xff09;&#xff0c;我便一口气将 Resnet50 中剩下的算法都写完了。 然后&#xff0c;暴力的&#xff0c;按照 Resnet50 的结构&#xff0c;将手写的…

【Flowable】Flowable基础表结构

1.表结构讲解 表结构创建文件&#xff1a;flowable-engine-6.3.0.jar!\org\flowable\db\create\flowable.mysql.create.engine.sql 工作流程的相关操作都是操作存储在对应的表结构中&#xff0c;为了能更好的弄清楚Flowable的实现原理和细节&#xff0c;我们有必要先弄清楚Fl…

Python边缘检测之prewitt, sobel, laplace算子

文章目录 滤波算子简介具体实现测试 滤波算子简介 ndimage中提供了卷积算法&#xff0c;并且建立在卷积之上&#xff0c;提供了三种边缘检测的滤波方案&#xff1a;prewitt, sobel以及laplace。 在convolve中列举了一个用于边缘检测的滤波算子&#xff0c;统一维度后&#xf…