Redis 为何使用近似 LRU 算法淘汰数据,而不是真实 LRU?

0f5556e35ae4036e060af253ca15d711.gif

作者 | 码哥呀

来源 | CSDN博客

在《Redis 数据缓存满了怎么办?》我们知道 Redis 缓存满了之后能通过淘汰策略删除数据腾出空间给新数据。

淘汰策略如下所示:

b7cd52e1c286d8f5467adf17e3634bdf.png

redis内存淘汰

设置过期时间的 key

volatile-ttl、volatile-random、volatile-lru、volatile-lfu 这四种策略淘汰的数据范围是设置了过期时间的数据。

所有的 key

allkeys-lru、allkeys-random、allkeys-lfu 这三种淘汰策略无论这些键值对是否设置了过期时间,当内存不足都会进行淘汰。

这就意味着,即使它的过期时间还没到,也会被删除。当然,如果已经过了过期时间,即使没有被淘汰策略选中,也会被删除。

volatile-ttl 和 volatile-randon 很简单,重点在于 volatile-lru 和 volatile-lfu,他们涉及到 LRU 算法 和 LFU 算法。

今天带大家一起搞定 Redis 的 LRU 算法…

近似 LRU 算法

❝ 什么是 LRU 算法呢?

LRU 算法的全程是 Least Rencently Used,顾名思义就是按照最近最久未使用的算法进行数据淘汰。

核心思想「如果该数据最近被访问,那么将来被访问的几率也更高」。

我们把所有的数据组织成一个链表:

  • MRU:表示链表的表头,代表着最近最常被访问的数据;

  • LRU:表示链表的表尾,代表最近最不常使用的数据。

09177b8fef64f92b8f41c8e8e460b1dc.png

LRU 算法

可以发现,LRU 更新和插入新数据都发生在链表首,删除数据都发生在链表尾

被访问的数据会被移动到 MRU 端,被访问的数据之前的数据则相应往后移动一位。

❝ 使用单链表可以么?

如果选用单链表,删除这个结点,需要 O(n) 遍历一遍找到前驱结点。所以选用双向链表,在删除的时候也能 O(1) 完成。

❝ Redis 使用该 LRU 算法管理所有的缓存数据么?

不是的,由于 LRU 算法需要用链表管理所有的数据,会造成大量额外的空间消耗。

除此之外,大量的节点被访问就会带来频繁的链表节点移动操作,从而降低了 Redis 性能。

所以 Redis 对该算法做了简化,Redis LRU 算法并不是真正的 LRU,Redis 通过对少量的 key 采样,并淘汰采样的数据中最久没被访问过的 key。

这就意味着 Redis 无法淘汰数据库最久访问的数据。

Redis LRU 算法有一个重要的点在于可以更改样本数量来调整算法的精度,使其近似接近真实的 LRU 算法,同时又避免了内存的消耗,因为每次只需要采样少量样本,而不是全部数据。

配置如下:

maxmemory-samples 50

运行原理

大家还记得么,数据结构 redisObject 中有一个 lru 字段, 用于记录每个数据最近一次被访问的时间戳。

typedef struct redisObject {unsigned type:4;unsigned encoding:4;/* LRU time (relative to global lru_clock) or* LFU data (least significant 8 bits frequency* and most significant 16 bits access time).*/unsigned lru:LRU_BITS;int refcount;void *ptr;
} robj;

Redis 在淘汰数据时,第一次随机选出 N 个数据放到候选集合,将 lru 字段值最小的数据淘汰。

再次需要淘汰数据时,会重新挑选数据放入第一次创建的候选集合,不过有一个挑选标准:进入该集合的数据的 lru 的值必须小于候选集合中最小的 lru 值。

如果新数据进入候选集合的个数达到了 maxmemory-samples 设定的值,那就把候选集合中 lru 最小的数据淘汰。

这样就大大减少链表节点数量,同时不用每次访问数据都移动链表节点,大大提升了性能。

Java 实现 LRU Cahce

LinkedHashMap 实现

完全利用 Java 的LinkedHashMap实现,可以采用组合或者继承的方式实现,在这里使用组合的形式完成。

public class LRUCache<K, V> {private Map<K, V> map;private final int cacheSize;public LRUCache(int initialCapacity) {map = new LinkedHashMap<K, V>(initialCapacity, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > cacheSize;}};this.cacheSize = initialCapacity;}
}

重点在于 LinkedHashMap的第三个构造函数上,要把这个构造参数accessOrder设为 true,代表LinkedHashMap内部维持访问顺序。

另外,还需要重写removeEldestEntry(),这个函数如果返回true,代表把最久未被访问的节点移除,从而实现淘汰数据。

自己实现

其中代码是从 LeetCode 146. LRU Cache 上摘下来的。代码里面有注释。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** 在链头放最久未被使用的元素,链尾放刚刚添加或访问的元素*/
class LRUCache {class Node {int key, value;Node pre, next;Node(int key, int value) {this.key = key;this.value = value;pre = this;next = this;}}private final int capacity;// LRU Cache的容量private Node dummy;// dummy节点是一个冗余节点,dummy的next是链表的第一个节点,dummy的pre是链表的最后一个节点private Map<Integer, Node> cache;//保存key-Node对,Node是双向链表节点public LRUCache(int capacity) {this.capacity = capacity;dummy = new Node(0, 0);cache = new ConcurrentHashMap<>();}public int get(int key) {Node node = cache.get(key);if (node == null) return -1;remove(node);add(node);return node.value;}public void put(int key, int value) {Node node = cache.get(key);if (node == null) {if (cache.size() >= capacity) {cache.remove(dummy.next.key);remove(dummy.next);}node = new Node(key, value);cache.put(key, node);add(node);} else {cache.remove(node.key);remove(node);node = new Node(key, value);cache.put(key, node);add(node);}}/*** 在链表尾部添加新节点** @param node 新节点*/private void add(Node node) {dummy.pre.next = node;node.pre = dummy.pre;node.next = dummy;dummy.pre = node;}/*** 从双向链表中删除该节点** @param node 要删除的节点*/private void remove(Node node) {node.pre.next = node.next;node.next.pre = node.pre;}
}

参考文献

https://redis.io/docs/manual/eviction/

http://antirez.com/news/109

https://time.geekbang.org/column/article/294640 

https://halfrost.com/lru_lfu_interview/

https://blog.csdn.net/csdlwzy/article/details/95635083

72eaba96c6ed94f834c71da2d0a5d50e.gif

往期推荐

Kubernetes 上调试 distroless 容器

Kubernetes 上容器的启动顺序如何把控?

Redis 内存满了怎么办?这样置才正确!

用 Spring boot 简单搭建一个微服务项目

73d579603ca5ce95a528913c35012494.gif

点分享

07647a30a6c9c37a824ed3f6acedd8c9.gif

点收藏

5b1b1dad8c953eb08062f9776d44c104.gif

点点赞

027bc7a9cd56ebeea67140182b2ffd67.gif

点在看

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

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

相关文章

量化感知训练实践:实现精度无损的模型压缩和推理加速

简介&#xff1a;本文以近期流行的YOLOX[8]目标检测模型为例&#xff0c;介绍量化感知训练的原理流程&#xff0c;讨论如何实现精度无损的实践经验&#xff0c;并展示了量化后的模型能够做到精度不低于原始浮点模型&#xff0c;模型压缩4X、推理加速最高2.3X的优化效果。 1. 概…

此表单只能填写一次_暴雪战网国服账号修改邮箱只能填写表单申请

暴雪战网国服账号只认身份信息&#xff0c;注册必须实名&#xff0c;而且实名信息千万不要乱填&#xff0c;不然账号出现问题&#xff0c;需要上传证件图片的&#xff0c;客服会核实与注册实名内容是否一致&#xff0c;不然无法帮助玩家解决一些问题。国服账号邮箱没有什么权限…

贾扬清演讲实录:一个AI开发者的奇幻漂流

简介&#xff1a;2021阿里灵杰AI工程化峰会&#xff0c;贾扬清深度解读阿里灵杰大数据和AI一体化平台。 演讲人&#xff1a;贾扬清 演讲主题&#xff1a;一个AI开发者的奇幻漂流 活动&#xff1a;2021阿里灵杰AI工程化峰会 对于绝大多数人来说&#xff0c;这一波AI浪潮兴许…

上云避坑指南100篇|「云」上玩法虽多,小心水土不服

商业智能BI发展至今&#xff0c;从市场增速来看&#xff0c;我国已进入 BI 及 DA&#xff08;数据分析&#xff09;领域的第一方阵&#xff0c;并成为发展最快的国家之一。 IDC 数据显示&#xff0c;2020 年中国商业智能软件市场规模为 5.8 亿美元&#xff0c;同比增长 17.1%&a…

如何基于LSM-tree架构实现一写多读

简介&#xff1a;传统MySQL基于binlog复制的主备架构有它的局限性&#xff0c;包括存储空间有限&#xff0c;备份恢复慢&#xff0c;主备复制延迟等问题&#xff0c;为了解决用户对于云上RDS(X-Engine)大容量存储&#xff0c;以及弹性伸缩的诉求&#xff0c;PolarDB推出了历史库…

Dubbo-go v3.0 正式发布 ——打造国内一流开源 Go 服务框架

简介&#xff1a;Dubbo-go 是常新的&#xff0c;每年都在不断进化。介绍 Dubbo-go 3.0 工作之前&#xff0c;先回顾其过往 6 年的发展历程&#xff0c;以明晰未来的方向。 作者 | 李志信 来源 | 阿里技术公众号 作者介绍&#xff1a; 李志信&#xff08;github laurencelizhix…

谁还没经历过死锁呢?

作者 | 敖丙来源 | 敖丙之前刚学习多线程时&#xff0c;由于各种锁的操作不当&#xff0c;经常不经意间程序写了代码就发生了死锁&#xff0c;不是在灰度测试的时候被测出来&#xff0c;就是在代码review的时候被提前发现。这种死锁的经历不知道大家有没有&#xff0c;不过怎么…

阿里巴巴超大规模Kubernetes基础设施运维体系解读

简介&#xff1a;ASI&#xff1a;Alibaba Serverless infrastructure&#xff0c;阿里巴巴针对云原生应用设计的统一基础设施。ASI 基于阿里云公共云容器服务 ACK之上&#xff0c;支撑集团应用云原生化和云产品的Serverless化的基础设施平台。 作者 | 仔仁、墨封、光南 来源 | …

搜索NLP行业模型和轻量化客户定制

简介&#xff1a;开放搜索NLP行业模型和轻量化客户定制方案&#xff0c;解决减少客户标注成本、完全无标注或少量简单标注的等问题&#xff0c;让搜索领域扩展更易用。 特邀嘉宾&#xff1a; 徐光伟&#xff08;昆卡&#xff09;--阿里巴巴算法专家 搜索NLP算法 搜索链路 …

CICD 的供应链安全工具 Tekton Chains

作者 | Addo Zhang来源 | 云原生指北软件供应链是指进入软件中的所有内容及其来源&#xff0c;简单地可以理解成软件的依赖项。依赖项是软件运行时所需的重要内容&#xff0c;可以是代码、二进制文件或其他组件&#xff0c;也可以是这些组件的来源&#xff0c;比如存储库或者包…

python计算不规则图形面积_python opencv中的不规则形状检测和测量

正如我在评论中提到的那样,对于这个问题,分水岭似乎是一个很好的方法.但是当你回答时,定义标记的前景和背景是困难的部分&#xff01;我的想法是使用形态梯度沿着冰晶获得良好的边缘并从那里开始工作;形态梯度似乎很有效.import numpy as npimport cv2img cv2.imread(image.pn…

深度解析开源推荐算法框架EasyRec的核心概念和优势

简介&#xff1a;如何通过机器学习PAI实现快速构建推荐模型 作者&#xff1a;程孟力 - 机器学习PAI团队 随着移动app的普及&#xff0c;个性化推荐和广告成为很多app不可或缺的一部分。他们在改善用户体验和提升app的收益方面带来了巨大的提升。深度学习在搜广推领域的应用也…

助力公益数字化 火山引擎向公益机构捐赠多款技术产品

5月18日&#xff0c;字节跳动公益联合火山引擎举办了“科技应用创新让公益更美好”线上交流会&#xff0c;与中国红十字基金会、壹基金等多家公益机构探讨如何利用科技信息化产品提升公益事业的效率&#xff0c;从而进一步解决社会问题。 交流会上&#xff0c;火山引擎联合Pic…

云效发布策略指南|滚动、分批、灰度怎么选?

简介&#xff1a;在日常和用户交流过程中&#xff0c;我们也经常会被用户问到关于发布的问题&#xff0c;比如不同职能团队之间应该如何配合、发布的最佳实践应该是什么样子的等等。今天我们就来聊聊常见应用发布方式的选择&#xff0c;以及每种发布模式适合什么样的场景。 无论…

shell安装mysql5.7_一键部署----shell脚本安装MySQL5.7

运维开发网 https://www.qedev.com2020-11-09 12:30出处&#xff1a;51CTO作者&#xff1a;wx5ddda4c97f426一键部署----shell脚本安装MySQL5.7#/bin/bashyum-yinstallncursesbisoncmakegccgcc-cncurses-develuseraddmysql-s/sbin/nologinread-p"输入你存放压缩包的绝对路…

极致用云,数智护航

简介&#xff1a;我们邀请到了阿里云混合云监控平台(Sunfire)团队负责人王肇刚来给我们分析下阿里背后的数字化业务运维安全工程标准及解决方案。 本次分享涵盖了全新发布的数字化业务运维安全工程标准、安全生产解决方案&#xff0c;以及全新升级的产品能力&#xff1a;包括了…

Lakehouse 架构解析与云上实践

简介&#xff1a;本文整理自 DataFunCon 2021大会上&#xff0c;阿里云数据湖构建云产品研发陈鑫伟的分享&#xff0c;主要介绍了 Lakehouse 的架构解析与云上实践。 作者简介&#xff1a;陈鑫伟&#xff08;花名熙康&#xff09;&#xff0c;阿里云开源大数据-数据湖构建云产品…

菜鸟教程 mysql like_MySQL LIKE 子句

MySQL LIKE 子句我们知道在 MySQL 中使用 SQL SELECT 命令来读取数据&#xff0c;同时我们可以在 SELECT 语句中使用 WHERE 子句来获取指定的记录。WHERE 子句中可以使用等号 来设定获取数据的条件&#xff0c;如 "runoob_author RUNOOB.COM"。但是有时候我们需要获…

云原生 Serverless Database 使用体验

简介&#xff1a;表格存储 Tablestore 作为一款广泛应用 Serverless DataBase&#xff0c;能够提供经济的计费模式&#xff0c;可以大幅缩减业务成本的同时&#xff0c; 具备极致的弹性服务能力和完全零运维的特性&#xff0c;能够给用户带来更丝滑的使用体验。 作者 | 李欣 …

首推业人一体,北森2022春季发布会,正式发布iTalentX5.0

5月19日&#xff0c;中国最大的一体化HR SaaS及人才管理平台北森于线上召开“HR x业务”2022春季新品发布会&#xff0c;基于“业务人力一体化”理念发布iTalentX5.0&#xff0c;以战略和业务为牵引&#xff0c;用数字化平台赋能经理、员工和HRBP&#xff0c; 实现人力资源与业…