图解缓存淘汰算法 LRU、LFU | 最近最少使用、最不经常使用算法 | go语言实现

news2024/11/24 2:54:34

写在前面

无论是什么系统,在研发的过程中不可避免的会使用到缓存,而缓存一般来说我们不会永久存储,但是缓存的内容是有限的,那么我们如何在有限的内存空间中,尽可能的保留有效的缓存信息呢? 那么我们就可以使用 LRU/LFU算法 ,来维持缓存中的信息的时效性。

LRU 详解

原理

LRU (Least Recently Used:最近最少使用)算法在缓存写满的时候,会根据所有数据的访问记录,淘汰掉未来被访问几率最低的数据。也就是说该算法认为,最近被访问过的数据,在将来被访问的几率最大。

流程如下:
在这里插入图片描述
假设我们有这么一块内存,一共有26个数据存储块。

  1. 当我们连续插入A、B、C、…Z的时候,此时内存已经插满
  2. 那么当我们再插入一个6,那么此时会将内存存放时间最久的数据A淘汰掉。
  3. 当我们从外部读取数据C的时候,此时C就会提到头部,这时候C就是最晚淘汰的了。

其实流程来说很简单。我们来拆分一下的话,不难发现这就是在维护一个双向链表

代码实现

定义一个存放的数据块结构

type item struct {
	key   string
	value any

	// the frequency of key
	freq int
}

定义LRU算法的结构体

type LRU struct {
	dl       *list.List // 维护的双端队列
	size     int // 当前的容量
	capacity int // 限定的容量

	storage map[string]*list.Element // 存储的key
}

获取某个key的value的函数,如果存在这个key,那么我们就把这个值移动到最前面MoveToFront,否则返回一个nil。

func (c *LRU) Get(key string) any {
	v, ok := c.storage[key]
	if ok {
		c.dl.MoveToFront(v)
		return v.Value.(item).value
	}

	return nil
}

当我们需要put进去一些东西的时候。会分以下几个步骤

  1. 是否已经存在,如果已经存在则,直接返回,并且将key移动到最前面。
  2. 如果没有存在,但是已经是到极限容量了,就把最后一个Back(),淘汰掉,然后在塞入。
  3. 塞入的话,是塞入到最前面PushFront
func (c *LRU) Put(key string, value any) {
	e, ok := c.storage[key]
	if ok {
		n := e.Value.(item)
		n.value = value
		e.Value = n
		c.dl.MoveToFront(e)
		return
	}

	if c.size >= c.capacity {
		e = c.dl.Back()
		dk := e.Value.(item).key
		c.dl.Remove(e)
		delete(c.storage, dk)
		c.size--
	}

	n := item{key: key, value: value}
	c.dl.PushFront(n)
	ne := c.dl.Front()
	c.storage[key] = ne
	c.size++
}

以上就是LRU算法的所有内容了,那我们看一下LFU算法。

LFU

原理

LFU全称是最不经常使用算法(Least Frequently Used),LFU算法的基本思想和所有的缓存算法一样,一定时期内被访问次数最少的页,在将来被访问到的几率也是最小的。

相比于LRU(Least Recently Use)算法,LFU更加注重于使用的频率LRU是其实可以看作是频率为1的LFU的。

在这里插入图片描述

和LRU不同的是,LFU是根据频率排序的,当我们插入的时候,一般会把新插入的放到链表的尾部,因为新插入的一定是没有出现过的,所以频率都会是1 , 所以会放在最后。

所以LFU的插入顺序如下:

  1. 如果A没有出现过,那么就会放在双向链表的最后,依次类推,就会是Z、Y。。C、B、A的顺序放到频率为1的链表中。
  2. 当我们新插入 A,B,C 那么A,B,C就会到频率为2的链表中
  3. 如果再次插入A,B那么A,B会在频率为3中。C依旧在2中
  4. 如果此时已经满了 ,新插入一个的话,我们会把最后一个D移除,并插入 6

在这里插入图片描述

代码

定义一个LFU的结构体:

// LFU the Least Frequently Used (LFU) page-replacement algorithm
type LFU struct {
	len     int // length
	cap     int // capacity
	minFreq int // The element that operates least frequently in LFU

	// key: key of element, value: value of element
	itemMap map[string]*list.Element

	// key: frequency of possible occurrences of all elements in the itemMap
	// value: elements with the same frequency
	freqMap map[int]*list.List // 维护一个频率和list的集合
}

我们使用LFU算法的话,我们插入的元素就需要带上频率了

// initItem to init item for LFU
func initItem(k string, v any, f int) item {
	return item{
		key:   k,
		value: v,
		freq:  f,
	}
}

如果我们获取某个元素,那么这个元素如果存在,就会对这个元素的频率进行加1

// Get the key in cache by LFU
func (c *LFU) Get(key string) any {
	// if existed, will return value
	if e, ok := c.itemMap[key]; ok {
		// the frequency of e +1 and change freqMap
		c.increaseFreq(e)
		obj := e.Value.(item)
		return obj.value
	}

	// if not existed, return nil
	return nil
}

增加频率

// increaseFreq increase the frequency if element
func (c *LFU) increaseFreq(e *list.Element) {
	obj := e.Value.(item)
	// remove from low frequency first
	oldLost := c.freqMap[obj.freq]
	oldLost.Remove(e)
	// change the value of minFreq
	if c.minFreq == obj.freq && oldLost.Len() == 0 {
		// if it is the last node of the minimum frequency that is removed
		c.minFreq++
	}
	// add to high frequency list
	c.insertMap(obj)
}

插入key到LFU缓存中

  1. 如果存在就对频率加1
  2. 如果不存在就准备插入
  3. 如果溢出了,就把最少频率的删除
  4. 如果没有溢出,那么就放到最后
// Put the key in LFU cache
func (c *LFU) Put(key string, value any) {
	if e, ok := c.itemMap[key]; ok {
		// if key existed, update the value
		obj := e.Value.(item)
		obj.value = value
		c.increaseFreq(e)
	} else {
		// if key not existed
		obj := initItem(key, value, 1)
		// if the length of item gets to the top line
		// remove the least frequently operated element
		if c.len == c.cap {
			c.eliminate()
			c.len--
		}
		// insert in freqMap and itemMap
		c.insertMap(obj)
		// change minFreq to 1 because insert the newest one
		c.minFreq = 1
		// length++
		c.len++
	}
}

插入一个新的

// insertMap insert item in map
func (c *LFU) insertMap(obj item) {
	// add in freqMap
	l, ok := c.freqMap[obj.freq]
	if !ok {
		l = list.New()
		c.freqMap[obj.freq] = l
	}
	e := l.PushFront(obj)
	// update or add the value of itemMap key to e
	c.itemMap[obj.key] = e
}

找到最少的链表,并且删除

// eliminate clear the least frequently operated element
func (c *LFU) eliminate() {
	l := c.freqMap[c.minFreq]
	e := l.Back()
	obj := e.Value.(item)
	l.Remove(e)

	delete(c.itemMap, obj.key)
}

以上就是所有LFU的算法实现了。

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

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

相关文章

代码算法训练营day8 | 344.反转字符串、 541. 反转字符串II、卡码网:54.替换数字、151.翻转字符串里的单词、卡码网:55.右旋转字符串

day8: 344.反转字符串541. 反转字符串II卡码网:54.替换数字151.翻转字符串里的单词卡码网:55.右旋转字符串 344.反转字符串 题目链接 状态:ok 文档:programmercarl.com 思路: 对于这道题目可以选择直接用C里的一个库函…

<Linux> 线程的同步与互斥

目录 前言: 一、资源共享问题 (一)多线程并发访问 (二)临界资源与临界区 (三)“锁” 是什么 二、多线程抢票场景 (一)并发抢票 (二)并发访…

基于Matlab的车牌识别算法,Matlab实现

博主简介: 专注、专一于Matlab图像处理学习、交流,matlab图像代码代做/项目合作可以联系(QQ:3249726188) 个人主页:Matlab_ImagePro-CSDN博客 原则:代码均由本人编写完成,非中介,提供…

Oracle 临时表空间管理(Temporary Tablespace)

Oracle临时表空间(Temporary Tablespace)主要用来存储数据库运行中产生的临时对象,例如SQL排序结果集,临时表等,这些对象的生存周期只有会话。本文总结了Oralce中涉及临时表空间的管理和优化操作。 目录 一、临时表空…

Stargo 管理部署 Starrocks 集群

配置主机间 ssh 互信 ssh-copy-id hadoop02 ssh-copy-id hadoop03配置系统参数 ############################ Swap检查 ############################ echo 0 | sudo tee /proc/sys/vm/swappiness########################### 内核参数检查 ########################## echo…

全国农产品价格分析预测可视化系统设计与实现

全国农产品价格分析预测可视化系统设计与实现 【摘要】在当今信息化社会,数据的可视化已成为决策和分析的重要工具。尤其是在农业领域,了解和预测农产品价格趋势对于农民、政府和相关企业都至关重要。为了满足这一需求,设计并实现了全国农产…

JS引用类型

在JavaScript中,除了基本类型(如字符串、数字、布尔值、null和undefined)之外,还有引用类型。引用类型包括对象、数组和函数。它们在内存中的存储方式与基本类型不同,因为它们在内存中存储的是对实际数据的引用&#x…

LeetCode 7 / 100

哈希表、双指针 哈希表两数之和字母异位词分组最长连续序列 双指针移动零盛最多水的容器三数之和接雨水 LeetCode 1.两数之和 LeetCode 49. 字母异位词分组 LeetCode 128. 最长连续序列 LeetCode [283. 移动零](https://leetcode.cn/problems/move-zeroes/?envTypestudy-plan-…

计算机网络-概述

文章目录 1.2 因特网概述1.2.1 网络、互连网(互联网)和因特网1.2.2 因特网发展的三个阶段1.2.4 因特网的组成 1.3 三种交换方式1.3.1 电路交换1.3.2 分组交换1.3.3 报文交换1.3.4 三种方式对比 1.4 计算机网络的定义1.5 计算机网络的性能指标1.5.1 速率1…

DDR协议基础进阶(三)——(基本功能、初始化、MR寄存器)

DDR协议基础进阶(三)——(基本功能、初始化、MR寄存器) 一、DDR基本功能 DDR基本功能主要包括: 8-bit prefetch预取——8-bit,是指8位数据,即8倍芯片位宽的数据。由于DDR内部数据传输是32bit…

Spring Web MVC入门(2)

学习Spring MVC Postman介绍 在软件工程中, 我们需要具有前后端分离的思想, 以降低耦合性. 但是在测试后端代码时,我们还得写前端代码测试,这是个令人头疼的问题. 那么我们如何测试自己的后端程序呢, 这就用到了一个工具: Postman. 界面介绍: 传参的介绍 1.普通传参, 也就…

Android Studio 打包 Maker MV apk 详细步骤

一.使用RPG Make MV 部署项目,获取项目文件夹 这步基本都不会有问题: 二.安装Android Studio 安装过程参考教材就行了: https://blog.csdn.net/m0_62491877/article/details/126832118 但是有的版本面板没有Android的选项(勾…

SpringAI——Java生态接入LLM

最近,Spring官网发布了SpringAI,可点此查看https://spring.io/blog/2024/03/12/spring-ai-0-8-1-released,对于SpringAI的介绍,可看官方文档:https://spring.io/projects/spring-ai#overview。 本文将使用SpringAI配合…

QML 布局管理器之GridLayout

一.QML GridLayout介绍 在QML中,GridLayout是一种用于布局元素的容器。它允许您以网格形式组织和排列元素。要使用rowspan、columnspan、layoutFillWidth和rowSpacing属性,您可以将一个元素跨越多行和多列,并填充整个宽度,同时设置…

Python Web开发记录 Day12:Django part6 用户登录

名人说:东边日出西边雨,道是无晴却有晴。——刘禹锡《竹枝词》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 1、登录界面2、用户名密码校验3、cookie与session配置①cookie与session②配置…

unity学习(60)——选择角色界面--MapHandler2-MapHandler.cs

1.新建一个脚本&#xff0c;里面有static变量loadingPlayerList using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace Assets.Scripts.Model {internal class LoadData{public static List<Pl…

Qt QTableWidget 实现行选中及行悬浮高亮

表格整行的 selected、hover 高亮需求很常见&#xff0c;但使用 Qt 提供的开箱即用的方法根本无法实现这个需求&#xff08;至少在当前的时间节点是不行的&#xff09;&#xff1b;想要实现这个效果必须要费一点点力气&#xff0c;我们尽量选择较为简单的方法。 话不多说&…

Ubuntu 20.04 系统如何优雅地安装NCL?

一、什么是NCL&#xff1f; NCAR Command Language&#xff08;NCL&#xff09;是由美国大气研究中心&#xff08;NCAR&#xff09;推出的一款用于科学数据计算和可视化的免费软件。 它有着非常强大的文件输入和输出功能&#xff0c;可读写netCDF-3、netCDF-4 classic、HDF4、b…

基于Struts开发物流配送(快递)管理系统

开发工具&#xff1a;EclipseJdkTomcatMySQL数据库

手机中的8款万能App推荐!

目录 1.全能AI工具箱——HuluAI 2.AI视频生成——巨日禄 3.全能办公套件——鲸鲮Office 4.视频音频转换器——VideotoMP3Converter 5.特效滤镜摄影——PicsArt 6.智能工具箱——SmartTools 7.手机视频编辑软件——KineMaster 8.安卓版万能文档阅读器——AllDocumentRea…