当前位置: 首页 > news >正文

【算法】缓存淘汰算法

目录

  • 1.概述
  • 2.代码实现
    • 2.1.FIFO
    • 2.2.LRU
    • 2.3.LFU
    • 2.4.Clock
    • 2.5.Random
  • 3.应用

1.概述

缓存淘汰策略是指在缓存容量有限的情况下,当缓存空间不足时决定哪些缓存项应当被移除的策略。缓存淘汰策略的目标是尽可能地保持缓存命中率高,同时合理地利用有限的缓存空间

需要注意的是,下面的代码实现只是对缓存淘汰算法的基本实现,在实际情况中,可以需要考虑更多的因素!

2.代码实现

2.1.FIFO

(1)FIFO (First-In-First-Out) 是一种基本的内存淘汰策略。其思路是按照元素的进入顺序来选择要淘汰的元素。具体来说,当有新的元素要加入到固定容量的缓存中时,如果缓存已满,就需要选择一个元素进行淘汰,以腾出空间存储新的元素。在 FIFO 策略中,选择被缓存时间最长的元素进行淘汰

(2)FIFO 策略维护一个队列,在每次新元素加入缓存时,将新元素添加到队列的末尾。当需要淘汰元素时,选择队列的头部元素作为淘汰对象,即最早进入缓存的元素。通过这种方式,始终保持最早进入缓存的元素在队列头部,最新进入缓存的元素在队列末尾。

(3)使用 FIFO 策略的好处是它的实现简单且执行效率高。然而,它没有考虑元素的访问频率或重要性等因素,只根据进入缓存的顺序来进行淘汰,可能会导致缓存中的数据不够优化。因此,在某些应用场景下,FIFO 策略可能不是最优选择,需要根据实际需求选择更复杂的内存淘汰策略。其具体代码实现如下:

class FIFOCache<K, V> {private int capacity;private Deque<K> queue;private Map<K, V> cache;//进行初始化操作public FIFOCache(int capacity) {this.capacity = capacity;this.queue = new ArrayDeque<>(capacity);this.cache = new HashMap<>(capacity);}//接收一个键 key 并返回相应的值,如果键不存在,则返回 nullpublic V get(K key) {return cache.getOrDefault(key, null);}//接收一个键 key 和一个值 value,并将它们存储在缓存中public void put(K key, V value) {if (!cache.containsKey(key)) {//如果缓存已满,将使用队列的 poll 方法移除最早加入的键if (queue.size() == capacity) {K oldestKey = queue.poll();cache.remove(oldestKey);}//然后将新的键加入队列的尾部queue.offer(key);}//将新的键值对加入缓存cache.put(key, value);}public V remove(K key) {//从队列中移除指定键queue.remove(key); // 从缓存中移除指定键并返回对应的值return cache.remove(key); }public void clear() {//清空队列queue.clear(); //清空缓存cache.clear(); }public int size() {return cache.size();}
}

2.2.LRU

参考 146.LRU 缓存这篇文章。

2.3.LFU

参考 460.LFU 缓存这篇文章。

2.4.Clock

(1)Clock 缓存淘汰算法是一种基于近似“最近未使用” (Not Recently Used) 策略的淘汰算法。该算法通过维护一个环形指针数组 (Clock),来判断缓存项是否被使用,从而进行淘汰决策。Clock 缓存淘汰算法的思路如下:

  • 对于每个缓存项,维护一个额外的访问位来标记缓存项是否被访问过。
  • 初始状态下,将所有缓存项的访问位都设置为 0。
  • 创建一个环形指针数组,数组中的每个槽位对应一个缓存项,并按照某种顺序排列。
  • 当需要淘汰一个缓存项时,根据指针指向的槽位判断:
    • 如果该槽位的访问位为 0,表示该缓存项最近未被使用,可以选择淘汰。
    • 如果该槽位的访问位为 1,表示该缓存项最近被使用过,将访问位置为 0,并将指针向后移动一位。
  • 重复第 4 步,直到找到一个访问位为 0 的槽位,将该缓存项置换出来,让出空间给新的缓存项。
  • 如果需要访问某个缓存项时,将其对应的访问位置为 1,表示该缓存项已被使用。

(2)Clock 缓存淘汰算法相对于经典的最近未使用 (LRU) 算法具有更低的时间和空间复杂度。它通过近似地追踪缓存项的访问状态来进行淘汰决策,适用于中小规模的缓存系统。

(3)然而,需要注意的是,Clock 算法可能出现缓存项的“反复使用”情况,即缓存项被不断地替换出去又被重新引入,这可能会影响缓存的命中率。因此,在实际应用中,需要根据具体场景和需求,综合考虑各个因素,选择合适的缓存淘汰策略。其具体代码实现如下:

class ClockCache<K, V> {//循环链表节点static class CircleListNode<K, V> {K key;V value;boolean accessFlag;CircleListNode<K, V> pre;CircleListNode<K, V> next;public CircleListNode() {}public CircleListNode(K key, V value) {this.key = key;this.value = value;}}private int capacity;//头节点private CircleListNode<K, V> dummyHead;private Map<K, CircleListNode<K, V>> cache;public ClockCache(int capacity) {this.capacity = capacity;this.dummyHead = new CircleListNode<>();this.dummyHead.next = this.dummyHead;this.dummyHead.pre = this.dummyHead;this.cache = new HashMap<>();}public V get(K key) {if (cache.containsKey(key)) {CircleListNode<K, V> node = cache.get(key);//将访问位设置为 truenode.accessFlag = true;return node.value;} else {return null;}}public void put(K key, V value) {if (cache.containsKey(key)) {CircleListNode<K, V> node = cache.get(key);//将访问位设置为 truenode.accessFlag = true;node.value = value;} else {if (cache.size() >= capacity) {//从最老的元素开始,此处直接从 head.next 开始,后续可以考虑优化记录这个 keyCircleListNode<K, V> node = this.dummyHead;boolean removeFlag = false;while (node.next != this.dummyHead) {//下一个元素node = node.next;if (!node.accessFlag) {//未访问,直接淘汰removeNode(node);System.out.println(node.key);removeFlag = true;break;} else {//设置当前 accessFlag 为 false,继续遍历下一个node.accessFlag = false;}}if (!removeFlag) {//如果循环一遍都没找到,直接取第一个元素即可CircleListNode<K, V> firstNode = this.dummyHead.next;System.out.println(firstNode.key);removeNode(firstNode);}}CircleListNode<K, V> newNode = new CircleListNode<>(key, value);newNode.accessFlag = true;CircleListNode<K, V> tail = dummyHead.pre;tail.next = newNode;newNode.pre = tail;newNode.next = dummyHead;dummyHead.pre = newNode;cache.put(key, newNode);}}public void remove(K key) {CircleListNode<K, V> node = cache.get(key);if (node != null) {cache.remove(key);removeNode(node);}}public void clear() {cache.clear();}public int size() {return cache.size();}private void removeNode(CircleListNode<K, V> node) {CircleListNode<K, V> pre = node.pre;CircleListNode<K, V> next = node.next;pre.next = next;next.pre = pre;cache.remove(node.key);}
}

2.5.Random

(1)Random(随机)内存淘汰算法的思想是基于随机选择的策略来进行缓存淘汰。该算法不依赖于缓存项的访问频率或时间等信息,而是通过随机选择一个缓存项进行淘汰,没有明确的优先级或规则。Random 内存淘汰算法的思想如下:

  • 当缓存空间不足时,需要淘汰一个缓存项。
  • 使用随机数生成器(如 Random 类)来生成一个随机索引,范围为缓存的容量。
  • 根据生成的随机索引,随机选择一个缓存项进行淘汰。
  • 被选择的缓存项被移除,让出空间给新的缓存项。

(2)随机选择的特点使得每个缓存项被淘汰的概率相等,没有明确的优先级,所有缓存项都有被淘汰的可能性。这种随机性的特点适用于一些无规律或无明确访问模式的缓存使用场景。然而,随机内存淘汰算法可能导致缓存命中率下降,因为被频繁访问的缓存项有可能被随机选中被淘汰,从而增加缓存不命中的概率。

因此,在选择淘汰算法时,需要根据具体应用场景和缓存使用模式来权衡各种算法的优劣,并选择适合的淘汰策略以达到最优的性能。

(3)其具体代码实现如下:

class RandomCache<K, V> {private int capacity;private List<K> keys;private Map<K, V> cache;private Random random;public RandomCache(int capacity) {this.capacity = capacity;this.keys = new ArrayList<>(capacity);this.cache = new HashMap<>(capacity);this.random = new Random();}//接收一个键 key 并返回相应的值,如果键不存在,则返回 nullpublic V get(K key) {return cache.getOrDefault(key, null);}public void put(K key, V value) {if (!cache.containsKey(key)) {//如果缓存已满,将使用 Random 对象的 nextInt 方法随机选择一个键索引并从列表中移除键if (keys.size() == capacity) {int index = random.nextInt(capacity);K randomKey = keys.remove(index);cache.remove(randomKey);}keys.add(key);}cache.put(key, value);}public V remove(K key) {if (cache.containsKey(key)) {//从列表中移除指定键keys.remove(key); //从缓存中移除指定键并返回对应的值return cache.remove(key);}return null;}public void clear() {keys.clear(); cache.clear(); }public int size() {return cache.size();}
}

3.应用

Redis 的缓存淘汰策略如下:

在这里插入图片描述
有关上面淘汰策略的一些具体说明如下:

  • noevction 是 Redis 的默认配置。当缓存被写满时,再有写请求进来,Redis 不再提供服务,直接返回错误。
  • LRULFU 算法是常见的淘汰算法,其具体细节可以参考 146.LRU 缓存、460.LFU 缓存这两篇文章。
  • random 指随机删除,相关的算法实现可以参考 380. O(1) 时间插入、删除和获取随机元素这篇文章。
  • volatile-ttl 策略:针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的数据越先被淘汰,即 ttl 越小的数据越优先被淘汰,这里的 ttl 指 Time to Live,即生存时间。

要想设置 Redis 的缓存淘汰策略,可以在其配置文件 redis.conf 中进行 maxmemory-policy 具体淘汰策略 的设置,例如设置淘汰策略为 volatile-lru

maxmemory-policy volatile-lru

相关文章:

【算法】缓存淘汰算法

目录 1.概述2.代码实现2.1.FIFO2.2.LRU2.3.LFU2.4.Clock2.5.Random 3.应用 1.概述 缓存淘汰策略是指在缓存容量有限的情况下&#xff0c;当缓存空间不足时决定哪些缓存项应当被移除的策略。缓存淘汰策略的目标是尽可能地保持缓存命中率高&#xff0c;同时合理地利用有限的缓存…...

接手项目要做的事项

总结&#xff1a;在接手别人的项目时&#xff0c;至少应该自己整理并绘画四个图 1、产品脑图&#xff1a;帮助你理解产品的功能&#xff1b; 2、UML时序图&#xff1a;帮助你源代码的核心技术实现&#xff1b; 3、整体业务泳道图&#xff1a;帮助你从整体上熟悉业务的流程&a…...

【Web】攻防世界Web_php_wrong_nginx_config

这题考察了绕过登录、目录浏览、后门利用 进来先是一个登录框&#xff0c;随便怎么输前端都直接弹窗 禁用js后再输入后登录 查看源码&#xff0c;好家伙&#xff0c;不管输什么都进不去 直接扫目录 访问/robots.txt 访问/hint.php 访问/Hack.php 抓包看一下 cookie里isLogin0…...

Flume采集Kafka并把数据sink到OSS

安装环境 Java环境, 略 (Flume依赖Java)Flume下载, 略Scala环境, 略 (Kafka依赖Scala)Kafak下载, 略Hadoop下载, 略 (不需要启动, 写OSS依赖) 配置Hadoop 下载JindoSDK(连接OSS依赖), 下载地址Github 解压后配置环境变量 export JINDOSDK_HOME/usr/lib/jindosdk-x.x.x expo…...

flutter,uni-app开发调试ios

一、申请ios开发者账号 二、ios开发者配置 ios 开发者需要配置的地方 https://developer.apple.com/account/resources/certificates/list Certificates&#xff08;证书&#xff09;: 作用&#xff1a; 证书用于对应用程序和开发者进行身份验证&#xff0c;确保安全性和可…...

MybatisBatchUtils功能介绍

MybatisBatchUtils 是一个 MyBatis 框架的工具类&#xff0c;主要用于简化 MyBatis 中批量操作的代码编写。该工具类封装了 MyBatis 中的批量操作方法&#xff0c;可以方便地进行批量插入、更新和删除等操作。 一般来说&#xff0c;使用 MyBatis 进行批量操作需要先设置 JDBC 驱…...

Flutter使用flutter_gen管理资源文件

pub地址&#xff1a; https://pub.dev/packages/flutter_gen 1.添加依赖 在你的pubspec.yaml文件中添加flutter_gen作为开发依赖 dependencies:build_runner:flutter_gen_runner: 2.配置pubspec.yaml 在pubspec.yaml文件中&#xff0c;配置flutter_gen的参数。指定输出路…...

vue3 setup语法糖,常用的几个:defineProps、defineEmits、defineExpose、

vue3和vue2组件之间传参的不同 <script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。 <script setup> 中的代码会在每次组件实例被创建的时候执行。 任何在 <script setup> 声明的顶层的绑定 (包括变量&#xff0c;函数声明&#xff0…...

JC/T 2087-2011建筑装饰用仿自然面艺术石检测

建筑装饰用仿自然面艺术石是指以硅酸盐水泥、轻质骨料为主要原料经浇筑成型的饰面装饰材料。 JC/T 2087-2011建筑装饰用仿自然面艺术石测试&#xff1a; 测试项目 测试方法 外观质量 GB/T 18601 尺寸偏差 GB/T 18601 体积密度 GB/T 9966.3 吸水率 GB/T 9966.3 压缩强…...

C语言——写一个简单函数,找两个数中最大者

#include <stdio.h>int max( int a, int b ) { return a>b ? a:b; }int main() { int a, b;printf("输入两个数:\n");scanf("%d %d", &a, &b);printf("max %d\n", max(a, b));return 0; }输出结果&#xff1a;...

机器学习中的混淆矩阵

混淆矩阵是用于评估分类模型性能的表格&#xff0c;它展示了模型在不同类别上的预测情况。对于二分类问题&#xff0c;混淆矩阵的构成如下&#xff1a; 假设有两个类别&#xff1a;正例&#xff08;Positive&#xff09;和负例&#xff08;Negative&#xff09;。 真正例&…...

QT基础实践之简易计算器

文章目录 简易计算器源码分享演示图第一步 界面设计第二步 设置槽第三步 计算功能实现 简易计算器 源码分享 链接&#xff1a;https://pan.baidu.com/s/1Jn5fJLYOZUq77eNJ916Kig 提取码&#xff1a;qwer 演示图 第一步 界面设计 这里直接用了ui界面&#xff0c;如果想要自己…...

南大通用 GBase 8s数据库级别权限

对于所有有权使用指定数据库的用户都必须赋予其数据库级别的用户权限。在GBase 8s 中&#xff0c;数据库级别的用户权限有三种&#xff0c;按权限从低到高排列依次为&#xff1a;CONNECT、RESOURCE、DBA。 1. CONNECT 这是级别最低的一种数据库级别用户权限。拥有该权限的用户…...

对话式数据需求激增,景联文科技提供高质量多轮对话数据定制采集标注服务

大模型的快速发展使得数据服务需求激增&#xff0c;产品整体处于供不应求状态。对话式数据集成为当下需求热点&#xff0c;人们对于更复杂、更真实的多轮对话数据需求不断增加&#xff0c;定制化服务占据市场需求主流。 通过对多轮对话数据的训练&#xff0c;模型可以更好地理解…...

python第1天之常识及环境安装

前言&#xff1a; 当谈到编程语言的流行度时&#xff0c;Python绝对是其中之一。Python是一种高级编程语言&#xff0c;其语法简单易懂&#xff0c;适用于各种不同的应用领域&#xff0c;包括Web开发、数据分析、人工智能等。在本文中&#xff0c;我们将探讨一些关于Pyth…...

中国高纯石英砂行业市场研究与投资前景报告(2024版)

内容简介&#xff1a; 高纯石英砂纯度高、品质好&#xff0c;生产的石英制品具有耐高温、耐腐蚀、低热膨胀性、高度绝缘性和透光性等优异的物理化学属性&#xff0c;被广泛用于光伏、电子、高端电光源、薄膜材料、国防科技等领域&#xff0c;是高端制造行业不可替代的原辅材料…...

遭到美国做空机构“灰熊”做空后,人工智能公司商汤科技股价暴跌

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;在遭到美国做空机构Grizzly Research&#xff08;灰熊&#xff09;指控夸大收入后&#xff0c;商汤科技的股价在周二一度下跌了9.7%。 Grizzly Research在周二发布的一份报告中称&#xff0c;商汤…...

异常数据检测 | Python实现孤立森林(IsolationForest)异常检测

孤立森林(IsolationForest)异常检测 IsolationForest[6]算法它是一种集成算法(类似于随机森林)主要用于挖掘异常(Anomaly)数据,或者说离群点挖掘,总之是在一大堆数据中,找出与其它数据的规律不太符合的数据。该算法不采样任何基于聚类或距离的方法,因此他和那些基于距离的的…...

营销互动类小游戏策划与开发

制定并开发一款营销互动小游戏需要经过一系列策划和实施步骤。以下是一个基本的流程&#xff0c;你可以根据自己的具体情况进行调整&#xff1a; 明确目标&#xff1a;确定小游戏的目标&#xff0c;是提高品牌知名度、增加销售、促进用户互动还是其他目标。 了解目标受众&…...

主机的容器化技术介绍

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 一、什么是容器 容器是一个标准化的单元&#xff0c;是一种轻量级、可移植的软件打包技术&#xff0c;容器将软件代码及其相关依赖打包&#xff0c;使应用程序可以在任何计算介质运行。例如开发人员在自己的…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能

指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...

《信号与系统》第 6 章 信号与系统的时域和频域特性

目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...

k8s从入门到放弃之Pod的容器探针检测

k8s从入门到放弃之Pod的容器探针检测 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;容器探测是指kubelet对容器执行定期诊断的过程&#xff0c;以确保容器中的应用程序处于预期的状态。这些探测是保障应用健康和高可用性的重要机制。Kubernetes提供了两种种类型…...

Java中栈的多种实现类详解

Java中栈的多种实现类详解&#xff1a;Stack、LinkedList与ArrayDeque全方位对比 前言一、Stack类——Java最早的栈实现1.1 Stack类简介1.2 常用方法1.3 优缺点分析 二、LinkedList类——灵活的双端链表2.1 LinkedList类简介2.2 常用方法2.3 优缺点分析 三、ArrayDeque类——高…...