缓存淘汰算法LRU-K,2Q(Two queues)

2023-11-03 07:40

本文主要是介绍缓存淘汰算法LRU-K,2Q(Two queues),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.LRU-K

1.1 简介

LRU-K中的K代表最近使用的次数,LRU可以当作LRU-1。LRU-K主要目的是为了解决LRU算法的缓存污染问题。

什么是缓存污染?

当数据访问次数非常少,甚至只会被访问一次,数据服务完访问请求后还继续留在缓存中,白白占用缓存空间,这就是缓存污染。

1.2 原理

相比LRU,LRU-K除了缓存队列还要维护一个访问历史队列,这个队列不缓存数据,仅记录数据的历史访问次数。当数据访问次数达到k次时,数据才放入缓存队列。历史队列、缓存队列的维护与LRU中缓存队列的维护一样,遵循LRU原则。
在这里插入图片描述

1.3 过程

(1). 数据第一次被访问,加入到访问历史列表;

(2). 如果数据在访问历史列表里后没有达到K次访问,则按照一定规则(FIFO,LRU)淘汰;

(3). 当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序;

(4). 缓存数据队列中被再次访问后,重新排序;

(5). 需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。

LRU-K具有LRU的优点,同时能够避免LRU的缺点,实际应用中LRU-2是综合各种因素后最优的选择,LRU-3或者更大的K值命中率会高,但适应性差,需要大量的数据访问才能将历史访问记录清除掉。

1.4 实现

// 直接继承我们前面写好的LRUCache
public class LRUKCache extends LRUCache {private int k; // 进入缓存队列的评判标准private LRUCache historyList; // 访问数据历史记录public LRUKCache(int cacheSize, int historyCapacity, int k) {super(cacheSize);this.k = k;this.historyList = new LRUCache(historyCapacity);}@Overridepublic Integer get(Integer key) {// 记录数据访问次数Integer historyCount = historyList.get(key);historyCount = historyCount == null ? 0 : historyCount;historyList.put(key, ++historyCount);return super.get(key);}@Overridepublic Integer put(Integer key, Integer value) {if (value == null) {return null;}// 如果已经在缓存里则直接返回缓存中的数据if (super.get(key) != null) {return super.put(key, value);;}// 如果数据历史访问次数达到上限,则加入缓存Integer historyCount = historyList.get(key);historyCount = historyCount == null ? 0 : historyCount;if (historyCount >= k) {// 移除历史访问记录historyList.remove(key);return super.put(key, value);}}
}
————————————————
版权声明:本文为CSDN博主「沙滩de流沙」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41231928/article/details/120593579

2. 2Q (Tow Queues)

2.1 简介

2Q算法类似LRU-2,不同点在于2Q将LRU-2算法中的访问历史队列(仅作记录不缓存数据)改为FIFO缓存队列。即,2Q算法有两个缓存队列,一个FIFO队列,一个LRU队列。

2.2 原理

当数据第一次被访问时,2Q算法将数据缓存在FIFO队列中,数据被第二次访问时,从FIFO队列中移除并添加至LRU队列中,两个队列按照自己的方法淘汰数据。
在这里插入图片描述
(1). 新访问的数据插入到FIFO队列;

(2). 如果数据在FIFO队列中一直没有被再次访问,则最终按照FIFO规则淘汰;

(3). 如果数据在FIFO队列中被再次访问,则将数据移到LRU队列头部;

(4). 如果数据在LRU队列再次被访问,则将数据移到LRU队列头部;

(5). LRU队列淘汰末尾的数据。

参考:
LRU-K和2Q缓存算法介绍
面试官:你知道 LRU算法 —— 缓存淘汰算法吗?

这篇关于缓存淘汰算法LRU-K,2Q(Two queues)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/336639

相关文章

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Redis解决缓存击穿问题的两种方法

《Redis解决缓存击穿问题的两种方法》缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击,本文给大家介绍了Re... 目录引言解决办法互斥锁(强一致,性能差)逻辑过期(高可用,性能优)设计逻辑过期时间引言缓存击穿:给

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR

一文详解Nginx的强缓存和协商缓存

《一文详解Nginx的强缓存和协商缓存》这篇文章主要为大家详细介绍了Nginx中强缓存和协商缓存的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、强缓存(Strong Cache)1. 定义2. 响应头3. Nginx 配置示例4. 行为5. 适用场景二、协商缓存(协

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为