深入理解LRU缓存算法:原理、应用与优化

LRU算法(Least Recently Used,最近最少使用算法)的思想是基于"时间局部性"原理,即在一段时间内,被访问过的数据在未来仍然会被频繁访问的概率较高。

LRU 原理

LRU算法的主要思想是将最近被使用的数据保留在缓存中,而最久未被使用的数据则被替换出去。它维护一个缓存空间,当需要替换数据时,选择缓存中最久未被使用的数据进行替换。

具体实现时,LRU算法通常使用一种数据结构,比如双向链表(Doubly Linked List)和哈希表(Hash Table)的组合来实现。每个节点在双向链表中保存了数据的值,并且通过哈希表提供了快速的数据查找能力。

在这里插入图片描述

LRU 局部性场景

尽管LRU算法在许多情况下表现良好,但在某些特定情况下可能无法很好地适应,包括以下几种情况:

  1. 突发访问模式(Bursty Access Pattern)如果访问模式发生突变,例如某个数据在一段时间内被频繁访问,然后突然不再被访问,LRU算法可能无法及时将其替换出缓存。这是因为LRU算法仅根据最近使用的时间进行替换决策,而不考虑访问频率的变化。

  2. 热点数据(Hotspot Data)当存在少数数据被频繁访问,而其他数据很少被访问时,LRU算法可能无法很好地区分热点数据和冷门数据。即使某个数据被频繁访问,但如果它在缓存中的位置靠后,LRU算法可能会将其替换出去,从而导致频繁访问的数据被频繁地加载到缓存中,影响性能。

  3. 数据访问分布不均匀(Skewed Data Access Pattern)如果数据的访问分布不均匀,即部分数据被频繁访问而其他数据很少被访问,LRU算法可能无法很好地利用缓存空间。因为LRU算法只关注最近访问的数据,而不管数据的访问频率。这可能导致一些常用数据无法保持在缓存中,而被替换出去。

在这些情况下,可以考虑其他缓存替换算法,如LFU(Least Frequently Used,最不经常使用算法)或者根据具体需求选择其他算法的变种,以更好地适应实际的数据访问模式。

LRU 实现

LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。

  • 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的
  • 哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置

首先使用哈希表进行定位,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在O(1)的时间内完成 get或者 put 操作。具体的方法如下:

  • 对于get操作,首先判断 key 是否存在:.

    • 如果 key 不存在,则返回 -1;
    • 如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。
  • 对于put操作,首先判断 key 是否存在:

    • 如果 key不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
    • 如果 key 存在,则与get操作类似,先通过哈希表定位,再将对应的节点的值更新头value ,并将该节点移到双向链表的头部。

上述各项操作中,访问哈希表的时间复杂度为O(1),在双向链表的头部添加节点、在双向链表的尾部删除节点的复杂度也为O(1)。而将一个节点移到双向链表的头部,可以分成「删除该节点」和[在双向链表的头部添加节点」两步操作,都可以在O(1)时间内完成。

在双向链表的实现中,使用一个伪头部(dummy head)和伪尾部(dummy tail)标记界限,以避免对头尾指针额外的操作

public class LRUCache {
    private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
    private int size;
    private int capacity;
    private DLinkedNode head, tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        // 使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        // 如果 key 存在,先通过哈希表定位,再移到头部
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            // 如果 key 不存在,创建一个新的节点
            DLinkedNode newNode = new DLinkedNode(key, value);
            // 添加进哈希表
            cache.put(key, newNode);
            // 添加至双向链表的头部
            addToHead(newNode);
            ++size;
            if (size > capacity) {
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode tail = removeTail();
                // 删除哈希表中对应的项
                cache.remove(tail.key);
                --size;
            }
        }
        else {
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
            node.value = value;
            moveToHead(node);
        }
    }

    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

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

    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    private DLinkedNode removeTail() {
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }

    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;
        public DLinkedNode() {}
        public DLinkedNode(int _key, int _value) {key = _key; value = _value;}
    }
}

InnoDB LRU 原理

InnoDB将LRU链表分为两个部分,也就是所谓的old区young区

  • young区在链表的头部,存放经常被访问的数据页,可以理解为热数据
  • old区在链表的尾部,存放不经常被访问的数据页,可以理解为冷数据

这两个部分的交汇处称为midpoint,分区比例可以使用以下参数设置

show variables like 'innodb_old_blocks_pct';

InnoDB LRU 链表

数据页第一次被加载进Buffer Pool时在old区的头部。当这个数据页在old区,再次被访问到,会做如下的判断:如果这个数据页在LRU链表中的old区 存在的时间超过了1秒,就把它移动到young区

时间设置参数为innodb_old_blocks_time

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

redis的三大模式的演化及集群模式思考和总结

redis的三大模式&#xff0c;也是循序渐进。 1、主从复制 比如一开始的读写分离的&#xff0c;主从复制。 一个master&#xff0c;多个slave。 master进行写和 增量同步&#xff0c;slave负责读&#xff0c;和接收增量同步的信息。 这样压力减轻。 2、哨兵模式 这个推出…

如何通过VPN访问内网?

VPN&#xff08;Virtual Private Network&#xff09;是一种通过公共网络建立私有网络连接的技术&#xff0c;可以在不同地点的网络中建立安全通道&#xff0c;实现远程访问内网资源的目的。本文将介绍如何通过VPN访问内网&#xff0c;并介绍一款名为“天联”的VPN服务。 什么是…

ASP.NET Core 标识(Identity)框架系列(一):如何使用 ASP.NET Core 标识(Identity)框架创建用户和角色?

前言 ASP.NET Core 内置的标识&#xff08;identity&#xff09;框架&#xff0c;采用的是 RBAC&#xff08;role-based access control&#xff0c;基于角色的访问控制&#xff09;策略&#xff0c;是一个用于管理用户身份验证、授权和安全性的框架。 它提供了一套工具和库&…

软件设计—接口安全设计规范

1.token授权机制 2.https传输加密 3.接口调用防滥用 4.日志审计里监控 5.开发测试环境隔离&#xff0c;脱敏处理 6.数据库运维监控审计 软件项目相关全套精华资料包获取方式①&#xff1a;点我获取 获取方式②&#xff1a;本文末个人名片直接获取。

内网IP与外网IP关联关系连接过程

前言 我们每天都会访问各种各样的网站&#xff0c;比如淘宝&#xff0c;百度等等。不免会思考&#xff0c;我们的设备是如何连接上这些网址的呢&#xff1f;要想搞清楚这个问题&#xff0c;首先就得先搞清楚内网ip和外网ip的联系。 网络结构 如图&#xff0c;假设我们的计算机…

测试开发必备技能:Python多线程处理!

什么是进程 进程是执行中的程序 拥有独立地址空间&#xff0c;内存&#xff0c;数据栈等 操作系统统一管理 派生&#xff08;fork或spawn&#xff09;新进程 进程间通信&#xff08;IPC&#xff09;方式共享信息 什么是线程 同进程下执行&#xff0c;并共享相同的上下文 …

python运行报错 ModuleNotFoundError: No module named ‘speech_recognition‘解决方法

写在前面 自己的测试环境&#xff1a; Ubuntu 20.04 一、问题描述 运行 python 程序时遇到如下问题&#xff1a; Traceback (most recent call last):File "mix.py", line 10, in <module>import speech_recognition as sr ModuleNotFoundError: No module…

羊大师分享:每日一杯羊奶,健康生活我有秘诀

今天&#xff0c;我要为大家揭开羊奶那神秘的面纱&#xff0c;分享为何它被我誉为“液体黄金”&#xff0c;以及如何正确饮用。 首先&#xff0c;让我们看看羊奶为何如此独特&#xff1a; 丰富的营养&#xff1a;羊奶含有高质量的蛋白质和必不可少的氨基酸&#xff0c;它们对…

免费在线SSL证书格式转换工具,轻松完成SSL证书格式转换

在互联网时代&#xff0c;网站安全问题备受关注&#xff0c;SSL证书作为确保网站安全的重要工具&#xff0c;会部署在服务器上来实现HTTPS确保传输数据安全。然而SSL证书有PEM、JKS、PKCS等多种格式&#xff0c;市面上有Apache、Nginx、Tomcat、Windows Server&#xff08;IIS&…

Jmeter的json提取器(包括提取一个参数的所有值,提取多个参数

jmeter的json提取器&#xff08;包括提取多个参数&#xff0c;提取一个参数的所有值&#xff09;https://www.cnblogs.com/jxial/p/15175314.html 在工作中经常遇到这种情况&#xff0c;就是下一个接口需要引用上一个接口返回的值&#xff0c;普遍的方法就是运用后置提取器 常…

项目文章| Plant CellDAP-seq解析草莓NAC转录因子FvRIF的调控网络

DAP-seq是一种体外研究蛋白与DNA结合的技术&#xff0c;该技术利用麦胚乳表达体系表达目标蛋白然后与基因组DNA文库体外孵育&#xff0c;得到目标蛋白的结合信息。与ChIP-seq和CUT&Tag不同&#xff0c;DAP-seq不需要抗体&#xff0c;在植物中应用更为广泛。今天我们分享一篇…

c++的STL(8) -- queue

queue容器概述 queue容器实现了实现了和队列相同结构的容器。 如图&#xff0c;队列这种结构有两端: 队首和队尾。 对于队列&#xff0c;我们添加数据只能从队尾添加&#xff0c;删除数据和获取数据只能从队首删除。是一种先进先出的结构。 -- 当然读取数据也只能从队首或者…

RuntimeError: Error(s) in loading state_dict for ZoeDepth解决方案

本文收录于《AI绘画从入门到精通》专栏,订阅后可阅读专栏内所有文章,专栏总目录:点这里。 大家好,我是水滴~~ 本文主要介绍在 Stable Diffusion WebUI 中使用 ControlNet 的 depth_zoe 预处理器时,出现的 RuntimeError: Error(s) in loading state_dict for ZoeDepth 异常…

【Leetcode每日一题】模拟 - 数青蛙(难度⭐⭐)(51)

1. 题目解析 题目链接&#xff1a;1419. 数青蛙 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 一、模拟青蛙叫声的基本逻辑 在模拟青蛙叫声的过程中&#xff0c;我们需要遵循一定的规则来判断何时青蛙会发出声音。…

MySQL 优化总结

目标知识 MySQL执行流程图 MySQL 优化成本路线图 优化成本&#xff1a;硬件>系统配置>数据库表结构>SQL及索引。优化效果&#xff1a;硬件<系统配置<数据库表结构<SQL及索引。 MySQL 五大优化原则 减少数据返回&#xff1a;设置合理字段数据类型、启用压缩…

通往 AGI 的道路上,OpenAI 逐渐构建了全模态的工具集

几天前&#xff0c;OpenAI 公司官宣将发布一个名为“Voice Engine”的小规模模型&#xff0c;引起巨大的声浪。 该模型支持仅使用文本输入和单个 15 秒音频样本来生成与原始说话者非常相似的自然语音。可应用于“语音转录”、“语音克隆”、“语音翻译”等场景。 笔者感叹 AI …

HarmonyOS 开发-MpChart运动健康场景实践案例

介绍 MpChart是一个包含各种类型图表的图表库&#xff0c;主要用于业务数据汇总&#xff0c;例如销售数据走势图&#xff0c;股价走势图等场景中使用&#xff0c;方便开发者快速实现图表UI&#xff0c;MpChart主要包括线形图、柱状图、饼状图、蜡烛图、气泡图、雷达图、瀑布图…

Golang-Gin 框架写的免杀平台,内置分离、捆绑等多种BypassAV方式

Golang-Gin 框架写的免杀平台&#xff0c;内置分离、捆绑等多种BypassAV方式 Golang-Gin 框架写的免杀平台&#xff0c;内置分离、捆绑等多种BypassAV方式。 cool 时间线&#xff1a; Golang Gin 框架写的免杀平台- (2021.11.12)Golang Gin 框架写的免杀平台&#xff0c;更…

分享|人力RPO项目是什么?算得上蓝海项目吗?

在当今竞争激烈的商业环境中&#xff0c;企业为了降低成本、提高效率&#xff0c;纷纷寻求创新的人力资源解决方案。其中&#xff0c;人力RPO(Recruitment Process Outsourcing&#xff0c;招聘流程外包)项目逐渐受到广泛关注。那么&#xff0c;人力RPO项目究竟是什么呢?它是否…

40-软件部署实战(上):部署方案及负载均衡、高可用组件介绍

40-软件部署实战&#xff08;上&#xff09;&#xff1a;部署方案及负载均衡、高可用组件介绍 。 系统缺少高可用、弹性扩容等能力&#xff0c;是很脆弱的&#xff0c;遇到流量波峰、发布变更很容易出问题。在系统真正上线前&#xff0c;我们需要重新调整部署架构&#xff0c;来…