经典算法之LRU算法

news2024/11/19 1:48:44

一、理论

LRU算法算是个常见的算法,很有必要去了解它,现在我们就来看看什么是 LRU

LRU 的全称是 Least Recently Used(最近最少使用),就如它的含义一样,最近最少使用的。在实际的场景中大多会把它当作一种 淘汰策略

它的应用场景也很简单,我们的存储空间总是有限的,一旦超过了最大限度就必须要淘汰一些数据,那淘汰哪种数据才算是合理的呢?合理的方式有很多,不同的场景也不一样,但淘汰 最近最少使用的数据,在绝大部分场景下都是合理的,所以LRU算法很常见。



二、实践


3-1、Redis 淘汰策略

redis的6大淘汰策略里面就有2种是 LRU策略

  1. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错

3-2、MyBtais 二级缓存

关于MyBatis的缓存可以参看这篇文章 https://blog.csdn.net/Tomwildboar/article/details/127570765

MyBatis的二级缓存各种功能实现是基于 装饰器模式来实现的,默认就是 LRU

下面是MyBatis 的 LruCache源码, 因为比较少就全部粘贴了

package org.apache.ibatis.cache.decorators;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.ibatis.cache.Cache;

/**
 * Lru (least recently used) cache decorator.
 *
 * @author Clinton Begin
 */
public class LruCache implements Cache {

  private final Cache delegate;
  private Map<Object, Object> keyMap;
  private Object eldestKey;

  public LruCache(Cache delegate) {
    this.delegate = delegate;
    setSize(1024);
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  public void setSize(final int size) {
    keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
      private static final long serialVersionUID = 4267176411845948333L;

      @Override
      protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
        boolean tooBig = size() > size;
        if (tooBig) {
          eldestKey = eldest.getKey();
        }
        return tooBig;
      }
    };
  }

  @Override
  public void putObject(Object key, Object value) {
    delegate.putObject(key, value);
    cycleKeyList(key);
  }

  @Override
  public Object getObject(Object key) {
    keyMap.get(key); // touch
    return delegate.getObject(key);
  }

  @Override
  public Object removeObject(Object key) {
    return delegate.removeObject(key);
  }

  @Override
  public void clear() {
    delegate.clear();
    keyMap.clear();
  }

  private void cycleKeyList(Object key) {
    keyMap.put(key, key);
    if (eldestKey != null) {
      delegate.removeObject(eldestKey);
      eldestKey = null;
    }
  }
}

它里面主要有两个操作

  1. 对 LinkedHashMap 的 removeEldestEntry 方法进行了重写
  2. 添加的时候会去调用 cycleKeyList 方法

removeEldestEntry

LinkedHashMap 底层也还是基于HashMap的,并且没有重写 put方法,是直接调用的 HashMap的put方法

在HashMap的put方法里面最后会去调用 afterNodeInsertion 方法, LinkedHashMap 里面重写了这个方法如下, evict 默认就是 true

这个方法的描述 possibly remove eldest ,可能移除最老的(也就是 first),当if判断是ture的时候就会删除这个元素

void afterNodeInsertion(boolean evict) { // possibly remove eldest
    LinkedHashMap.Entry<K,V> first;
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}

我们再来看看重写后的 removeEldestEntry ,eldest 通过上面其实就是链表头第一个元素

keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
    private static final long serialVersionUID = 4267176411845948333L;

    @Override
    protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
    boolean tooBig = size() > size;
        if (tooBig) {
            eldestKey = eldest.getKey();
        }
        return tooBig;
    }
};

如果当前容器里面的数据大于规定的容量,就执行 eldestKey = eldest.getKey();


cycleKeyList
添加元素的时候会调用这个方法

private void cycleKeyList(Object key) {
    keyMap.put(key, key);
    if (eldestKey != null) {
        delegate.removeObject(eldestKey);
        eldestKey = null;
    }
}

上面我们知道,当元素大于容量规定大小,eldestKey 就会等于第一个元素的key,这时候就会删掉这个元素,从而实现 LRU策略


3-3、MySQL bufferPool

上面这种LRU策略,在某种情况下会存在漏洞,如果我们每次都是淘汰第一个元素,那如果我们在查询到元素A后就一直不再使用它了,这样它从链表尾到链表尾才会被淘汰,但是我们有一个元素B在它的后面但是经常被使用,但元素B依旧比元素A先淘汰,这样就不合理了。

MySQL 操作数据的时候先会把数据加载到内存中(Buffer Pool),但是内存的大小是有限的,所以也就存在了淘汰策略,如果只是简单的使用LRU策略,就会存在上述的问题。

MySQL对LRU策略进行了改造来完善这个问题,完整的buffer pool 参看这里 https://blog.csdn.net/Tomwildboar/article/details/121525187

它的逻辑也很简单

  1. 把内存分为 冷数据区和热数据区,数据默认都是进入冷数据区
  2. 冷数据区的数据经过默认的时间间隔被访问就会进入热数据区(默认 1s)
  3. 热数据区也进行划分,前 1/4的热数据区访问的时候不会进行移动
  4. 后 3/4的热数据被访问的时候会被提到前 1/4的位置
  5. 淘汰的时候优先淘汰冷数据区

在这里插入图片描述

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

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

相关文章

CAN总线学习笔记 | STM32CubeMX配置CAN环回测试

CAN基础知识介绍文中介绍了CAN协议的基础知识&#xff0c;以及STM32F4芯片的CAN控制器相关知识&#xff0c;下面将通过实例&#xff0c;利用STM32CubeMX图形化配置工具&#xff0c;来实现CAN通讯的环回测试 一、STM32CubeMX配置 CAN是挂载在APB1总线上&#xff0c;设置PCLK1时…

Flink系列之Flink中四层Graph详解

title: Flink系列 四、Flink Runtime 四层 Graph 详解 首先回顾一下 Flink 的整体架构设计&#xff1a; {% asset_img processes.svg %} 关于上图中的一些概念的解释&#xff1a; 1、DataFlow Graph 是一个逻辑概念&#xff0c;表示这个应用程序的一个执行图&#xff0c;事…

开关电源环路笔记(11)-TL431电路的几个电阻的取值

上次文章发出来后&#xff0c;有兄弟留言布置了作业&#xff0c;让说说几个电阻的取值&#xff0c;就是下图的Rled&#xff0c;Rbias&#xff0c;R1&#xff0c;Rlower等。那么就写写吧&#xff0c;毕竟&#xff0c;这个电路确实用得非常多&#xff0c;实际工作中确实也需要知道…

登录信息记录模块实现(UserAgentUtils)

如果文章对你有帮助欢迎【关注❤️❤️❤️点赞&#x1f44d;&#x1f44d;&#x1f44d;收藏⭐⭐⭐】一键三连&#xff01;一起努力&#xff01; 一、UserAgentUtils简介 user-agent-utils 是一个用来解析 User-Agent 字符串的 Java 类库。 其能够识别的内容包括&#xff1a…

32_STM32内部温度传感器实验

目录 内部温度传感器简介 STM32ADC对应引脚 内部温度传感器使用注意使用事项 开启内部温度传感器步骤 实验源码 内部温度传感器简介 内部温度传感器框图 从图上可以看出温度传感器可通过TSVREFR控制位连接到ADC的固定通道16,温度的值最终肯定是被转换成电压值,电压值然后通…

学会python这十个语言技法,让你有上手风范

如何让你的代码更有python味&#xff1f; 大部分编程语言都有共性&#xff0c;也有个性。下手害怕个性&#xff0c;视为异端&#xff0c;抵触之&#xff1b;上手善用个性&#xff0c;欣欣然&#xff0c;妙用之。 1、三元表达式 别抱怨python没有三元表达式&#xff0c;请看&…

《大数据导论》之数据的概念、类型和组织形式

目录 说在前面 一、数据的概念 二、数据类型 ​三、数据组织形式 说在前面 大数据时代悄然来临&#xff0c;带来了整个信息技术发展的巨大变革&#xff0c;并深刻影响着社会生产和人们生活的方方面面。全球范围内&#xff0c;世界各国政府也非常重视整个大数据的研究和产业…

hadoop pig-0.17.0 安装配置

下载Apache Pig 首先&#xff0c;从以下网站下载最新版本的Apache Pig&#xff0c;下载Pig步骤取自W3C&#xff1a;Pig安装教程 步骤1 打开Apache Pig网站的主页。在News部分下&#xff0c;点击链接release page&#xff08;释放&#xff09;&#xff0c;如下面的快照所示。 步…

解决git中出现的“bash syntax error near unexpected token ’(‘”错误

今天来分享一篇关于我在git使用过程中出现的一个错误。 错误信息&#xff1a; bash: syntax error near unexpected token (’ 翻译过来就是提示我在’(这里有错误&#xff0c;而这个错误是我在使用git commit提交时候产生的&#xff0c;我当时是这么写的&#xff1a; git c…

java中Optional的应用,以及map和flatMap的区别

关于Option的介绍可以看深入理解java8中的Optional 类就可以了&#xff0c;但是复杂一点的使用在网上却没有搜到&#xff0c;这里结合我开发时遇到的真实案例来讲一下Option的使用。 1.案例一 在真实业务操作过程中&#xff0c;都是对象里面套对象&#xff0c;这边先简单定义操…

flink window 框架详细解读

1. dataStream window group window time window 基于时间驱动 滚动时间窗口 tumble time window 滑动时间窗口 sliding time window 会话时间窗口 session time window count window 基于数据驱动 滚动计数窗口 tumble count window 滑动计数窗口 sliding count window 2. s…

皕杰报表之雷达图

雷达图&#xff08;又可称为戴布拉图、螂蛛网图&#xff09;是数据分析报表的一种。即将各项数据分析所得的数字或比率&#xff0c;就其比较重要的项目集中划在一个图表上&#xff0c;来表现一组数据的各项数据比率的情况&#xff0c;使用者能一目了然的了解这个数据的指标的变…

LeetCode中等题之通过最少操作次数使数组的和相等

题目 给你两个长度可能不等的整数数组 nums1 和 nums2 。两个数组中的所有值都在 1 到 6 之间&#xff08;包含 1 和 6&#xff09;。 每次操作中&#xff0c;你可以选择 任意 数组中的任意一个整数&#xff0c;将它变成 1 到 6 之间 任意 的值&#xff08;包含 1 和 6&#…

计算机组成大题分析(三)

假定计算机 M 字长为 16 位&#xff0c;按字节编址&#xff0c;连接 CPU 和主存的系统总线中地址线为 20 位、数据线为 8位&#xff0c;采用 16 位定长指今字&#xff0c;指令格式及其说明如下: 其中&#xff0c;op1-op3 为操作码&#xff0c;rs&#xff0c;t 和 rd 为通用寄存…

【计算机视觉+CNN】keras+ResNet残差网络实现图像识别分类实战(附源码和数据集 超详细)

需要源码和数据集请点赞关注收藏后评论区留言私信~~~ 一、深度卷积神经网络模型结构 1&#xff1a;LeNet-5 LeNet-5卷积神经网络首先将输入图像进行了两次卷积与池化操作&#xff0c;然后是两次全连接层操作&#xff0c;最后使用Softmax分类器作为多分类输出&#xff0c;它对…

MySQL插入汉字报错的解决方案

MySQL插入汉字报错的原因是字符集的问题&#xff0c;MySQL默认使用的是Latin&#xff08;拉丁文&#xff09;字符集&#xff0c;可以在创建数据库时指定其字符集&#xff1a;CREATE DATABASE test DEFAULT CHARACTER SET utf8 或者修改MySQL的配置文件&#xff0c;可以参考以下…

Qt事件循环嵌套,BlockingQueuedConnection与QWaitCondition比较

前言&#xff1a; 之前写过有关事件循环和条件变量的博客&#xff1a; Qt使用事件循环&#xff0c;信号&#xff0c;stop变量&#xff0c;sleep阻塞&#xff0c;QWaitConditionQMutex条件变量&#xff0c;退出子线程工作_大橘的博客-CSDN博客_qt stop函数 Qt事件循环&#x…

Unity3D导出Android工程中使用并交互

, 目录 1&#xff0c;版本信息 2&#xff0c;前期准备 Unity方面&#xff1a; Android方面&#xff1a; 3&#xff0c;Android与Unity3D交互 1&#xff0c;版本信息 unity2020 android studio 2021 *不要用android studio 2020系列&#xff0c;存在不能导入Library的b…

Spring学习 | Bean作用域生命周期

文章目录一、作用域1.1 xml文件中配置1.2 注解配置二、生命周期2.1 四个阶段2.2 添加后置处理器2.3 实现aware类型接口2.4 Bean 初始化的方式2.5 Bean 销毁的方式2.6 测试程序学习视频&#x1f3a5;&#xff1a;https://www.bilibili.com/video/BV1Vf4y127N5 一、作用域 ❓ 引入…

Linux系统中裸机按键中断的驱动方法

大家好&#xff0c;今天主要和大家聊一聊&#xff0c;如何实现按键中断的驱动​方法。 目录 ​第一&#xff1a;外部中断头文件实现 ​第二&#xff1a;外部中断源文件的具体实现 ​第三&#xff1a;编写对应的main.c函数 ​第一&#xff1a;外部中断头文件实现 #ifndef _…