经典算法之LRU算法
https://www.bilibili.com/video/BV1Z24y1k7cY
一、理论
LRU算法算是个常见的算法,很有必要去了解它,现在我们就来看看什么是 LRU
LRU 的全称是 Least Recently Used(最近最少使用),就如它的含义一样,最近最少使用的。在实际的场景中大多会把它当作一种 淘汰策略
它的应用场景也很简单,我们的存储空间总是有限的,一旦超过了最大限度就必须要淘汰一些数据,那淘汰哪种数据才算是合理的呢?合理的方式有很多,不同的场景也不一样,但淘汰 最近最少使用的数据,在绝大部分场景下都是合理的,所以LRU算法很常见。
二、实践
3-1、Redis 淘汰策略
redis的6大淘汰策略里面就有2种是 LRU策略
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- 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;}}
}
它里面主要有两个操作
- 对 LinkedHashMap 的 removeEldestEntry 方法进行了重写
- 添加的时候会去调用 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
它的逻辑也很简单
- 把内存分为 冷数据区和热数据区,数据默认都是进入冷数据区
- 冷数据区的数据经过默认的时间间隔被访问就会进入热数据区(默认 1s)
- 热数据区也进行划分,前 1/4的热数据区访问的时候不会进行移动
- 后 3/4的热数据被访问的时候会被提到前 1/4的位置
- 淘汰的时候优先淘汰冷数据区
经典算法之LRU算法相关推荐
- FIFO算法与LRU算法
#include<iostream.h> #include<stdio.h> #include<iomanip.h> const int Max = 10; ...
- 操作系统:页面置换算法(FIFO算法、LRU算法、LFU算法、NRU算法)实验报告
操作系统实验报告 一.实验名称 :页面置换算法 二.实验目的: 在实验过程中应用操作系统的理论知识. 三.实验内容: 采用C/C++编程模拟实现:FIFO算法.LRU算法.LFU算法.NRU算法四个页 ...
- FIFO算法、LRU算法与LFU算法
当从辅存调页至主存时,若主存已满时,需要进行主存页面之间的替换,虚拟存储器的替换算法有:FIFO算法.LRU算法.LFU算法等. FIFO算法: 先进先出调度算法.如果一个数据是最先进入的,那么可能认 ...
- 页面置换算法之 LRU算法
最近最久未使用(LRU)算法 基本思想: 利用局部性原理,根据一个作业在执行过程中过去的页面访问历史来推测未来的行为.它认为过去一段时间里不曾被访问过的页面,在最近的将来可能也不会再被访问.所以,这种 ...
- c语言实现FIFO算法和LRU算法,C语言实现FIFO算法与LRU算法
在操作系统中,当程序在运行过程中,若其所要访问的页面不再内存中而需要把他们调入内存,但内存已无空闲空间时,为了保证该进程能正常运行,系统必须从内存调出一页程序或数据送磁盘的兑换区中.但哪一个页面调出, ...
- C++实现LRU算法(LeetCode 146 LRU缓存机制)
LRU算法: LRU算法(Least Recently Used)是一种缓存淘汰策略,最近使用的数据是有用的, 如果缓存满了,删除最久没用过的数据 LRU算法描述: (1)设置缓存大小 (2)get: ...
- 计算机组成与体系结构 LRU 算法与 MRU 算法对比
计算机组成与体系结构,第7章存储器层次结构,关于Cache组相连映射的替换算法对比. LRU 算法与 MRU 算法对比 LRU 算法 定义 LRU(least-recently used)算法,即最近 ...
- 手把手带你拆解 LRU 算法
手把手带你拆解 LRU 算法 文章目录 手把手带你拆解 LRU 算法 概述 [146. LRU 缓存](https://leetcode-cn.com/problems/lru-cache/) LRU ...
- 01 guava-cache:LRU算法
文章目录 LRU算法介绍 LRU算法实现 使用LinkedHashMap实现 LRU算法介绍 最近最久未使用(LRU)算法,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也更高&q ...
最新文章
- 单片机异常复位后如何保存变量数据
- Java基础 ----常用时间类
- 华为 mysql实例监控,华为云文档数据库服务DDS监控告警全新优化
- Java-消息框显示两整数加减乘除
- 成功创业者所需的能力
- C语言程序设计教程的读后感,《高质量c语言编程》读后感
- 安卓逆向系列教程 4.1 字符串资源
- .NET、TensorFlow和Kaggle的风车
- 力扣-1337. 矩阵中战斗力最弱的 K 行
- 【C#设计模式——创建型模式】抽象工厂模式
- SQL Server 2008空间数据应用系列十:使用存储过程生成GeoRSS聚合空间信息
- Packet Tracer 思科模拟器入门教程
- MySQL深入浅出之索引
- Word里面如何在两个字正中间加入点
- Ruby on Rails,创建和执行migrations迁移文件
- 10月25日, win8 来了
- 区块链落地应用虚实待验,以人为本挖井为先
- 一切还算顺利,远方的你还好吗?
- 黑马训练营10届开学典礼
- android色温选择控件,ColorPicker