深入理解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

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

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

相关文章

UEditor编辑器自动将div标签转换成p标签应该如何解决 ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4

首先在ueditor的文件夹下找到ueditor.all.js文件&#xff0c;然后搜索allowDivTransToP找到这段代码&#xff0c;把后面的true设置为false 接着在ueditor.config.js文件内搜索allowDivTransToP找到如下的代码&#xff0c;将注释去掉并且改为false //默认过滤规则相关配置项目/…

ref和out的区别

区别 ref 需要初始化&#xff0c; out可以不用初始化 相同 都可以声明引用 ref static void Func(ref int value){value 9;}static void Main(string[] args){Func(1);int num1 8;Func(ref num); } out static void Func(out int value) {value 9; } static void M…

MPT - 原理及应用

前文回顾 Merkle原理及应用Merkle代码实现Patricia原理及应用Patricia代码实现 什么是MPT&#xff08;Merkle Patricia Tree&#xff09;树 MPT树是一种数据结构&#xff0c;用于在以太坊区块链中高效地存储和检索账户状态、交易历史和其他重要数据。MPT树的设计旨在结合Merk…

sqlmap(四)案例

一、注入DB2 http://124.70.71.251:49431/new_list.php?id1 这是墨者学院里的靶机&#xff0c;地址&#xff1a;https://www.mozhe.cn/ 1.1 测试数据库类型 python sqlmap.py -u "http://124.70.71.251:49431/new_list.php?id1" 1.2 测试用户权限类型 查询选…

java的flatMap和map的区别

在Java中&#xff0c;flatMap是Stream API的一个中间操作&#xff0c;它可以将一个流中的每个元素转换成另一个流&#xff0c;然后将所有新生成的流连接成一个单一的流。这个操作通常用于处理嵌套的集合结构&#xff0c;比如List<List<T>>或类似的复杂结构&#xf…

常见深度学习之十二大激活函数【函数定义、性质、数学公式、代码实现】

目录 前言 1、激活函数的定义与作用 2、激活函数的性质 二、常见的激活函数 2.1 Sigmoid函数&#xff1a; 1. 作用 2. 优点 3. 缺点 4. 数学公式 5.Sigmoid函数实现及可视化图像 2.2 Tanh函数 1. 函数定义 2.优点 3.缺点 4.Tanh函数实现及可视化图像 2.3ReLU 函数 &#xff1a;…

物联网实战--驱动篇之(二)Modbus协议

目录 一、modbus简介 二、功能码01、02 三、modbus解析 四、功能码03、04 五、功能码05 六、功能码06 七、功能码16 一、modbus简介 我们在网上查阅modbus的资料发现很多很杂&#xff0c;modbus-RTU ASCII TCP等等&#xff0c;还有跟PLC结合的&#xff0c;地址还分1开…

C语言进阶课程学习记录-第29课 - 指针和数组分析(下)

C语言进阶课程学习记录-第29课 - 指针和数组分析&#xff08;下&#xff09; 数组名与指针实验-数组形式转换实验-数组名与指针的差异实验-转化后数组名加一的比较实验-数组名作为函数形参小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课…

一篇SCP小故事

你有什么毛病&#xff1f;&#xff08;What is wrong with you?&#xff09;: --------------------------------------------------------------------------------------------------------------------------------- 我工作的时候把灯都关掉是有原因的。这个世界有无限的…

【JavaWeb】Day39.MySQL概述——数据库设计-DQL(二)

数据库设计-DQL 聚合函数 聚合函数查询就是纵向查询&#xff0c;它是对一列的值进行计算&#xff0c;然后返回一个结果值。&#xff08;将一列数据作为一个整体&#xff0c;进行纵向计算&#xff09; 语法&#xff1a; select 聚合函数(字段列表) from 表名 ; 注意 : 聚合…

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

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

每日三道面试题之 Java并发编程 (四)

1.什么是线程死锁 线程死锁是并发编程中一个常见问题&#xff0c;它发生在两个或多个线程永久性地阻塞彼此&#xff0c;等待对方释放锁&#xff0c;但没有任何一方先行释放锁的情况下。简单来说&#xff0c;每个线程都持有对方需要的资源而等待对方释放资源&#xff0c;导致所…

高校人事管理系统业务分析

目标用户 大学人事部门&#xff0c;部门、院系、任务 解决问题 人事部门按业务划分了很多科室、数据分散、工作流程杂乱、工作效率低。 主要功能模块 人事综合管理平台、个人自助服务平台、人才招聘管理系统、薪酬管理子系统、职称评审子系统、绩效考核子系统组成。

泛零售行业大会员经营的业务挑战与应对策略

​泛零售企业发展到成规模阶段一定会沉淀大量会员&#xff0c;在当前的市场竞争下&#xff0c;企业的经营重点在关注增量市场的同时&#xff0c;也会聚焦对存量会员的价值深挖&#xff0c;提升会员忠诚度&#xff0c;实现“以客户体验为中心、以数据驱动运营”。 对于多业态、…

R语言处理DNA等位基因不平衡(一)

在生物信息学和基因组学研究中&#xff0c;等位基因不平衡分析是一种重要的方法&#xff0c;用于识别在特定生物过程或疾病状态中可能受到选择压力的基因或基因区域。等位基因不平衡&#xff08;Allele Imbalance&#xff09;指的是基因座上两个等位基因表达或存在的比例不等&a…

小程序打开空白的问题处理

小程序打开是空白的&#xff0c;如下&#xff1a; 这个问题都是请求域名的问题&#xff1a; 一、检查服务器域名配置了 https没有&#xff0c;如果没有&#xff0c;解决办法是申请个ssl证书&#xff0c;具体看这里 https://doc.crmeb.com/mer/mer2/4257 二、完成第一步后&#…

基于springboot实现墙绘产品展示交易平台管理系统项目【项目源码+论文说明】

基于springboot实现墙绘产品展示交易平台系统演示 摘要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本墙绘产品展示交易平台就是在这样的大环境下诞生&#xff…

RTSP/Onvif视频安防监控平台EasyNVR调用接口返回匿名用户名和密码的原因排查

视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif协议接入&#xff0c;并能对接入的视频流进行处理与多端分发&#xff0c;包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。平台拓展性强、支持二次开发与集成&#xff0c;可应用在景区、校园、水利、社区、工地等场…

【小程序】常用方法、知识点汇总1

欢迎来到《小5讲堂》 这是《小程序》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言请求超时Markdown解析逐行显示效果文本变动事件转发…

Python 设计一个监督自己的软件2

们可以为这个日常任务记录和评分系统添加更多功能,使其更加丰富和实用。以下是一些可以考虑的功能: 用户登录和个人资料管理自定义任务和权重每日、每周、每月的任务统计和可视化任务提醒和待办事项列表成就系统和奖励机制社交分享和好友竞争 下面我们来逐步实现这些功能: 用…