https://www.bilibili.com/video/BV1Z24y1k7cY

一、理论

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);}@Overridepublic String getId() {return delegate.getId();}@Overridepublic 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;@Overrideprotected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {boolean tooBig = size() > size;if (tooBig) {eldestKey = eldest.getKey();}return tooBig;}};}@Overridepublic void putObject(Object key, Object value) {delegate.putObject(key, value);cycleKeyList(key);}@Overridepublic Object getObject(Object key) {keyMap.get(key); // touchreturn delegate.getObject(key);}@Overridepublic Object removeObject(Object key) {return delegate.removeObject(key);}@Overridepublic 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 eldestLinkedHashMap.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;@Overrideprotected 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. 淘汰的时候优先淘汰冷数据区

经典算法之LRU算法相关推荐

  1. FIFO算法与LRU算法

    #include<iostream.h> #include<stdio.h> #include<iomanip.h> const int Max = 10;     ...

  2. 操作系统:页面置换算法(FIFO算法、LRU算法、LFU算法、NRU算法)实验报告

    操作系统实验报告 一.实验名称 :页面置换算法 二.实验目的: 在实验过程中应用操作系统的理论知识. 三.实验内容: 采用C/C++编程模拟实现:FIFO算法.LRU算法.LFU算法.NRU算法四个页 ...

  3. FIFO算法、LRU算法与LFU算法

    当从辅存调页至主存时,若主存已满时,需要进行主存页面之间的替换,虚拟存储器的替换算法有:FIFO算法.LRU算法.LFU算法等. FIFO算法: 先进先出调度算法.如果一个数据是最先进入的,那么可能认 ...

  4. 页面置换算法之 LRU算法

    最近最久未使用(LRU)算法 基本思想: 利用局部性原理,根据一个作业在执行过程中过去的页面访问历史来推测未来的行为.它认为过去一段时间里不曾被访问过的页面,在最近的将来可能也不会再被访问.所以,这种 ...

  5. c语言实现FIFO算法和LRU算法,C语言实现FIFO算法与LRU算法

    在操作系统中,当程序在运行过程中,若其所要访问的页面不再内存中而需要把他们调入内存,但内存已无空闲空间时,为了保证该进程能正常运行,系统必须从内存调出一页程序或数据送磁盘的兑换区中.但哪一个页面调出, ...

  6. C++实现LRU算法(LeetCode 146 LRU缓存机制)

    LRU算法: LRU算法(Least Recently Used)是一种缓存淘汰策略,最近使用的数据是有用的, 如果缓存满了,删除最久没用过的数据 LRU算法描述: (1)设置缓存大小 (2)get: ...

  7. 计算机组成与体系结构 LRU 算法与 MRU 算法对比

    计算机组成与体系结构,第7章存储器层次结构,关于Cache组相连映射的替换算法对比. LRU 算法与 MRU 算法对比 LRU 算法 定义 LRU(least-recently used)算法,即最近 ...

  8. 手把手带你拆解 LRU 算法

    手把手带你拆解 LRU 算法 文章目录 手把手带你拆解 LRU 算法 概述 [146. LRU 缓存](https://leetcode-cn.com/problems/lru-cache/) LRU ...

  9. 01 guava-cache:LRU算法

    文章目录 LRU算法介绍 LRU算法实现 使用LinkedHashMap实现 LRU算法介绍 最近最久未使用(LRU)算法,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也更高&q ...

最新文章

  1. 单片机异常复位后如何保存变量数据
  2. Java基础 ----常用时间类
  3. 华为 mysql实例监控,华为云文档数据库服务DDS监控告警全新优化
  4. Java-消息框显示两整数加减乘除
  5. 成功创业者所需的能力
  6. C语言程序设计教程的读后感,《高质量c语言编程》读后感
  7. 安卓逆向系列教程 4.1 字符串资源
  8. .NET、TensorFlow和Kaggle的风车
  9. 力扣-1337. 矩阵中战斗力最弱的 K 行
  10. 【C#设计模式——创建型模式】抽象工厂模式
  11. SQL Server 2008空间数据应用系列十:使用存储过程生成GeoRSS聚合空间信息
  12. Packet Tracer 思科模拟器入门教程
  13. MySQL深入浅出之索引
  14. Word里面如何在两个字正中间加入点
  15. Ruby on Rails,创建和执行migrations迁移文件
  16. 10月25日, win8 来了
  17. 区块链落地应用虚实待验,以人为本挖井为先
  18. 一切还算顺利,远方的你还好吗?
  19. 黑马训练营10届开学典礼
  20. android色温选择控件,ColorPicker

热门文章

  1. 鲁大师 AIMARK 性能评测与 OpenVINO - 酷睿11代 i7 AI 性能
  2. 全面回顾硬盘保护技术
  3. python学习 - copy模块的浅复制(copy)与深复制(deepcopy)
  4. 尤里的复仇II 回归
  5. 通过Focas连接Fanuc的NC Guide
  6. php ci csrf,CI的CSRF的改造
  7. 一个语文老师的日记作文
  8. sem史上最全实验室安全说明书,别再用生命做实验!
  9. pdf.js javascript实现pdf阅读器
  10. 林超文大师PCB设计经验、技巧分享(一)