Redis缓存淘汰算法——LRU

文章目录

  • 一、LRU 算法概述
    • 1.1 LRU 算法的工作原理
    • 1.2 手写LRU
  • 二、Redis 中的 LRU 算法
    • 2.1 近似 LRU 算法
    • 2.2 如何判断“最近最少使用”的键?
    • 2.3 Redis 中的 LRU 配置

Redis 中, LRU(Latest Recently Used,最近最少使用) 是一种非常常见的缓存淘汰算法。它用于管理 Redis 的内存使用,当 Redis 达到最大内存限制时,决定哪些键应该被删除,从而腾出空间给新的数据。

一、LRU 算法概述

LRU(Least Recently Used,最近最少使用)算法是一种缓存淘汰算法,其核心思想是:在缓存空间有限的情况下,当缓存满时,应该淘汰最久没有被使用的数据。换句话说,LRU 算法会清除那些最久未被访问的缓存数据,以腾出空间存储新的数据。

1.1 LRU 算法的工作原理

  • 每当访问一个缓存项时(无论是读取还是写入),该缓存项都会被标记为最近访问过的。
  • 当缓存空间已满,需要淘汰数据时,LRU 算法会删除最近最少访问的数据项,即链表尾部的数据项。

以下是一个LRU算法示意图:
在这里插入图片描述

  1. 向一个缓存空间依次插入三个数据A/B/C,填满了缓存空间;
  2. 读取数据A一次,按照访问时间排序,数据A被移动到缓存头部;
  3. 插入数据D的时候,由于缓存空间已满,触发了LRU的淘汰策略,数据B被移出,缓存空间只保留了D/A/C

一般而言,LRU算法的数据结构不会如示意图那样,仅使用简单的队列或链表去缓存数据,而是会采用Hash表 + 双向链表的结构,利用Hash表确保数据查找的时间复杂度是O(1)双向链表又可以使数据插入/删除等操作也是O(1)

在这里插入图片描述

1.2 手写LRU

手写一个 LRU(Least Recently Used) 缓存实现,通常使用 哈希表(HashMap) 和 双向链表(Doubly Linked List) 的组合来实现。哈希表用于存储数据项的引用,双向链表用于维护数据项的访问顺序,链表头部表示最近访问的数据,链表尾部表示最久未访问的数据。

下面是一个简单的 LRU 缓存的手写实现,使用 Java 实现了基本的 get()put() 操作。

import java.util.HashMap;public class LRUCache {// 双向链表节点类class Node {int key;int value;Node prev;Node next;public Node(int key, int value) {this.key = key;this.value = value;}}private final int capacity;  // 缓存最大容量private HashMap<Integer, Node> cache;  // 存储数据private Node head, tail;  // 双向链表的头尾节点// 构造方法,初始化容量和头尾节点public LRUCache(int capacity) {this.capacity = capacity;this.cache = new HashMap<>();// 创建伪头尾节点,方便操作head = new Node(0, 0);tail = new Node(0, 0);// 初始化头尾节点连接head.next = tail;tail.prev = head;}// 获取数据public int get(int key) {Node node = cache.get(key);if (node == null) {System.out.println("缓存中没有该数据,key: " + key);return -1;  // 数据不存在,返回 -1}// 将访问的节点移动到链表头部moveToHead(node);System.out.println("访问数据,key: " + key + ", value: " + node.value);return node.value;}// 插入数据public void put(int key, int value) {Node node = cache.get(key);if (node == null) {// 如果缓存没有该数据,创建新节点Node newNode = new Node(key, value);cache.put(key, newNode);// 将新节点插入到链表头部addNode(newNode);System.out.println("插入新数据,key: " + key + ", value: " + value);if (cache.size() > capacity) {// 如果缓存超出容量,移除链表尾部节点Node tailNode = removeTail();cache.remove(tailNode.key);System.out.println("缓存已满,淘汰最久未使用的数据,key: " + tailNode.key);}} else {// 如果缓存已有该数据,更新值并移动到链表头部node.value = value;moveToHead(node);System.out.println("更新数据,key: " + key + ", 新值: " + value);}printCacheStatus();  // 打印当前缓存状态}// 将节点移动到链表头部(表示最近访问)private void moveToHead(Node node) {removeNode(node);addNode(node);}// 将节点添加到链表头部private void addNode(Node node) {node.prev = head;node.next = head.next;head.next.prev = node;head.next = node;}// 从链表中移除节点private void removeNode(Node node) {Node prev = node.prev;Node next = node.next;prev.next = next;next.prev = prev;}// 删除链表尾部节点(最久未使用的节点)private Node removeTail() {Node res = tail.prev;removeNode(res);return res;}// 打印当前缓存状态private void printCacheStatus() {System.out.print("当前缓存状态:");Node current = head.next;while (current != tail) {System.out.print("{" + current.key + "=" + current.value + "} ");current = current.next;}System.out.println();}// 测试public static void main(String[] args) {LRUCache cache = new LRUCache(2); // 设置缓存容量为 2cache.put(1, 1);  // 缓存: {1=1}cache.put(2, 2);  // 缓存: {1=1, 2=2}System.out.println("获取数据:key=1, 返回值=" + cache.get(1));  // 返回 1,缓存: {2=2, 1=1}cache.put(3, 3);  // 缓存: {1=1, 3=3},淘汰键 2System.out.println("获取数据:key=2, 返回值=" + cache.get(2));  // 返回 -1 (未找到)cache.put(4, 4);  // 缓存: {3=3, 4=4},淘汰键 1System.out.println("获取数据:key=1, 返回值=" + cache.get(1));  // 返回 -1 (未找到)System.out.println("获取数据:key=3, 返回值=" + cache.get(3));  // 返回 3,缓存: {4=4, 3=3}System.out.println("获取数据:key=4, 返回值=" + cache.get(4));  // 返回 4,缓存: {4=4, 3=3}}
}

输出:
在这里插入图片描述

二、Redis 中的 LRU 算法

Redis 实现 LRU 算法时并没有精确计算每个数据的使用顺序,而是采用了 近似算法 来提高效率。Redis 利用 采样算法 来模拟 LRU 行为,而不是每次访问时都更新所有元素的访问顺序,Redis 的 LRU 实现称为 LRU-K(LRU-K Sampling)

Redis内部只使用Hash表缓存了数据,并没有创建一个专门针对LRU算法的双向链表,之所以这样处理也是因为以下几个原因:

  • 筛选规则,Redis是随机抽取一批数据去按照淘汰策略排序,不再需要对所有数据排序;
  • 性能问题,每次数据访问都可能涉及数据移位,性能会有少许损失;
  • 内存问题,Redis对内存的使用一向很“抠门”,数据结构都很精简,尽量不使用复杂的数据结构管理数据;
  • 策略配置,如果线上Redis实例动态修改淘汰策略会触发全部数据的结构性改变,这个Redis系统无法承受的。

2.1 近似 LRU 算法

RedisLRU 近似算法是指每次访问数据时并不会更新所有元素的顺序,而是通过随机抽取一定数量的键,估算这些键的使用频率来决定哪些键应该被淘汰。这种近似的方式避免了每次都精确计算访问顺序,减少了性能开销。在缓存满的时候,Redis 选择最近最少使用的元素进行淘汰。

2.2 如何判断“最近最少使用”的键?

它的基本思路是

  • 定期从缓存中随机挑选一部分数据。
  • 对这些随机选中的数据,检查它们的使用情况,找出最久未使用的键进行淘汰。

采样的大小 是一个重要的参数。默认情况下,Redis 会随机抽取 10 个键,通过这些键来估算哪个是最近最少使用的。抽样的数量可以通过配置参数调整。

2.3 Redis 中的 LRU 配置

Redis 提供了两个关键的配置项来控制 LRU 淘汰策略:

  • maxmemory:配置 Redis 的最大内存。一旦Redis 的内存使用超过该阈值,Redis 会根据maxmemory-policy配置策略,执行内存淘汰操作。
  • maxmemory-policy:配置内存达到限制时,Redis 应该使用哪种淘汰策略。Redis 支持多种内存淘汰策略,其中与 LRU 相关的有以下几种:
    • allkeys-lru:对所有的键使用 LRU 淘汰策略,即当内存达到限制时,Redis 会移除最久未被访问的键。
    • volatile-lru:仅对设置了过期时间的键使用 LRU 淘汰策略,意味着只有那些设置了 TTL(过期时间)的键会根据 LRU 策略被淘汰。
    • allkeys-random:随机移除键,使用随机的方式淘汰数据。
    • volatile-random:仅删除那些设置了过期时间的键,并且使用随机策略。
    • volatile-ttl:仅删除设置了过期时间的键,并且删除那些即将过期的键。

示例:

maxmemory 100mb           # 设置最大内存为 100MB
maxmemory-policy allkeys-lru  # 设置内存达到限制时,使用 LRU 淘汰策略

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【原创工具】同文件夹PDF文件合并 By怜渠客

【原创工具】同文件夹PDF文件合并 By怜渠客 原贴&#xff1a;可批量合并多个文件夹内的pdf工具 - 吾爱破解 - 52pojie.cn 他这个存在一些问题&#xff0c;并非是软件内自主实现的PDF合并&#xff0c;而是调用的pdftk这一工具&#xff0c;但楼主并没有提供pdftk&#xff0c;而…

C# Combox 绑定数据

1.在界面中添加一个combox 2.将数据绑定到combox List<GrindingType> type new List<GrindingType>();type.Add(new GrindingType { Id 1, Name "Product A", Type new List<string> { "1", "2" } });type.Add(new Grin…

怎样能写出完美的Prompt

怎样能写出完美的Prompt 大模型发展Prompt 实测最后感受 大模型发展 随着语言大模型的智能化演进&#xff0c;其作为内容生产引擎的核心竞争力日益凸显。如何通过Prompt工程深度释放其潜能&#xff0c;实现工作效率的指数级提升与文本质量的突破性飞跃&#xff0c;本质上是对&…

【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的农村合作社招聘系统

开题报告 本文以服务新农村建设为背景&#xff0c;针对农村劳动力就业信息获取不充分、求职效率低下的问题&#xff0c;设计并实现了农村合作社招聘系统。该平台具备注册登录、个人信息管理、就业资讯发布与互动、岗位搜索、详细信息查看、岗位申请以及申请状态跟踪等功能。系…

数据结构与算法-图论-最短路-拓展运用

选择最佳路线 分析&#xff1a; 这是一道图论中的最短路径问题&#xff0c;目标是在给定的公交网络中&#xff0c;找到从琪琪家附近的车站出发&#xff0c;到她朋友家附近车站&#xff08;编号为 s &#xff09;的最短时间。以下是对该问题的详细分析&#xff1a; 问题关键信息…

鸿道Intewell操作系统的Linux实时拓展方案

在工业控制、智能制造、自动驾驶等领域&#xff0c;实时性一直是操作系统的核心挑战。Linux作为开源系统的代表&#xff0c;虽然具备生态丰富&#xff0c;功能强大的优势&#xff0c;但其内核调度机制与中断处理能力难以满足微秒级硬实时要求。针对这一痛点&#xff0c;鸿道Int…

搭建Nexus前端npm私服,上传发布npm包和下载依赖

1、创建repository 登录Nexus的管理页面&#xff0c;创建npm&#xff08;proxy&#xff09;和npm&#xff08;hosted&#xff09;&#xff0c;然后创建npm&#xff08;group&#xff09;将这两个repository包含进来。 1.1 创建npm&#xff08;proxy&#xff09; 选择npm&…

数组总结【代码随想录】

一.数组 1.lc 27移除数组中的重复元素 且必须仅使用 O(1) 额外空间并 原地 修改输入数组。 输入&#xff1a;nums [3,2,2,3], val 3 输出&#xff1a;2, nums [2,2] 解释&#xff1a;函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长…

大模型训练——pycharm连接实验室服务器

一、引言 我们在运行或者复现大佬论文代码的时候&#xff0c;笔记本的算力不够&#xff0c;需要使用实验室的服务器进行运行。可以直接在服务器的终端上执行&#xff0c;但是这样的话代码调试就不方便。而我们可以使用 pycharm 连接到服务器&#xff0c;既方便了代码调试&…

STM32的C语言软件延时函数

STM32的延时方法很多&#xff0c;其中采用定时器延时&#xff0c;可以得到较为精确的延时&#xff0c;但是有时对延时精度要求不高的场合&#xff0c;采用软件延时&#xff0c;也是必须的。特别是在RTOS系统中&#xff0c;使用SysTick的普通计数模式对延迟进行管理&#xff0c;…

前端网页或者pwa如何实现只横屏显示,设备竖着的时候依然保持横屏

开发的时候&#xff0c;就是以横屏的样式开发的&#xff0c;所以横屏的展示效果就是&#xff1a; 当设备竖着的时候&#xff0c;会进行缩放&#xff0c;展示效果不友好&#xff0c;所以需要设备竖着的时候&#xff0c;也横屏显示&#xff1a; 实现原理就是&#xff1a;使用css监…

计算机毕业设计SpringBoot+Vue.js电影评论网站系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Locale+Jackson导致Controller接口StackOverflowError异常解决

问题 由于参与的项目有出海需求&#xff0c;即需要给外国人使用&#xff0c;即&#xff1a;需要支持i18n&#xff08;Internationalization的缩写&#xff0c;共20个字母&#xff0c;除去首尾两个字母&#xff0c;中间有18个&#xff0c;故简称i18n&#xff09;。 本来是好的…

Graph and GNN——图的表示与图神经网络的介绍与应用

Hi&#xff0c;大家好&#xff0c;我是半亩花海。细数日子已然有很长一段时间没有更新博客了&#xff0c;不是在忙东忙西&#xff0c;就是在玩这玩那&#xff0c;在家摆&#xff0c;在学校gap&#xff0c;无敌了。言归正传&#xff0c;今天暂且先进一步探索并整理一部分图神经网…

京准电钟:NTP精密时钟服务器在自动化系统中的作用

京准电钟&#xff1a;NTP精密时钟服务器在自动化系统中的作用 京准电钟&#xff1a;NTP精密时钟服务器在自动化系统中的作用 NTP精密时钟服务器在自动化系统中的作用非常重要&#xff0c;特别是在需要高精度时间同步的场景中。NTP能够提供毫秒级的时间同步精度&#xff0c;这…

Https解决了Http的哪些问题

部分内容来源&#xff1a;小林coding 详细解析 Http的风险 HTTP 由于是明文传输&#xff0c;所以安全上存在以下三个风险&#xff1a; 1.窃听风险 比如通信链路上可以获取通信内容&#xff0c;用户号容易没。 2.篡改风险 比如强制植入垃圾广告&#xff0c;视觉污染&#…

GO 进行编译时插桩,实现零码注入

Go 编译时插桩 Go 语言的编译时插桩是一种在编译阶段自动注入监控代码的技术&#xff0c;目的是在不修改业务代码的情况下&#xff0c;实现对应用程序的监控和追踪。 基本原理 Go 编译时插桩的核心思想是通过在编译过程中对源代码进行分析和修改&#xff0c;将监控代码注入到…

flex布局自定义一行几栏,靠左对齐===grid布局

模板 <div class"content"><div class"item">1222</div><div class"item">1222</div><div class"item">1222</div><div class"item">1222</div><div class"…

微软推出Office免费版,限制诸多,只能编辑不能保存到本地

易采游戏网2月25日独家消息&#xff1a;微软宣布推出一款免费的Office版本&#xff0c;允许用户进行基础文档编辑操作&#xff0c;但限制颇多&#xff0c;其中最引人关注的是用户无法将文件保存到本地。这一举措引发了广泛讨论&#xff0c;业界人士对其背后的商业策略和用户体验…

NLP的预处理数据

处理文本数据的主要工具是Tokenizer。Tokenizer根据一组规则将文本拆分为tokens。然后将这些tokens转换为数字&#xff0c;然后转换为张量&#xff0c;成为模型的输入。模型所需的任何附加输入都由Tokenizer添加。 如果您计划使用预训练模型&#xff0c;重要的是使用与之关联的…