问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

Redis——LRU(LeastRecentlyUsed)详述

发布网友 发布时间:2024-09-30 16:24

我来回答

1个回答

热心网友 时间:2024-10-14 17:13

1、简介

Redis是基于内存存储的key-value数据库,我们知道内存虽然快但空间小,当物理内存达到上限时,系统就会跑的很慢,这是因为swap机制会将部分内存的数据转移到swap分区中,通过与swap的交换保证系统继续运行;但是swap属于硬盘存储,速度远远比不上内存,尤其是对于Redis这种QPS非常高的服务,发生这种情况是无法接收的。(注意如果swap分区内存也满了,系统就会发生错误!)

Linux操作系统可以通过free-m查看swap大小:

因此如何防止Redis发生这种情况非常重要(面试官问到Redis几乎没有不问这个知识点的)。

2、maxmemory配置

Redis针对上述问题提供了maxmemory配置,这个配置可以指定Redis存储器的最大数据集,通常情况都是在Redis.conf文件中进行配置,也可以运行时使用CONFIGSET命令进行一次性配置。

redis.conf文件中的配置项示意图:

默认情况maxmemory配置项并未启用,Redis官方介绍64位操作系统默认无内存*,32位操作系统默认3GB隐式内存配置,如果maxmemory为0,代表内存不受限。

因此我们在做缓存架构时,要根据硬件资源+业务需求做合适的maxmemory配置。

3、内存达到maxmemory怎么办

很显然配置了最大内存,当maxmemory达到了最大上限之后Redis不可能不干活了,那么Redis是怎么来处理这个问题的呢?这就是本文的重点,Redis提供了maxmemory-policy淘汰策略(本文只讲述LRU不涉及LFU,LFU在下一篇文章讲述),对满足条件的key进行删除,辞旧迎新。

maxmemory-policy淘汰策略:

noeviction:当达到内存*并且客户端尝试执行可能导致使用更多内存的命令时返回错误,简单来说读操作仍然允许,但是不准写入新的数据,del(删除)请求可以。

allkeys-lru:从全体key中,通过lru(LeastRecentlyUsed-最近最少使用)算法进行淘汰

allkeys-random:从全体key中,随机进行淘汰

volatile-lru:从设置了过期时间的全部key中,通过lru(LeastRecentlyUsed-最近最少使用)算法进行淘汰,这样可以保证未设置过期时间需要被持久化的数据,不会被选中淘汰

volatile-random:从设置了过期时间的全部key中,随机进行淘汰

volatile-ttl:从设置了过期时间的全部key中,通过比较key的剩余过期时间TTL的值,TTL越小越先被淘汰

还有volatile-lfu/allkeys-lfu这个留到下文一起探讨,两个算法不一样!

random随机淘汰只需要随机取一些key进行删除,释放内存空间即可;ttl过期时间小先淘汰也可以通过比较ttl的大小,将ttl值小的key进行删除,释放内存空间即可。

那么LRU是怎么实现的呢?Redis又是如何知道哪个key最近被使用了,哪个key最近没有被使用呢?

4、LRU算法实现

我们先用Java的容器实现一个简单的LRU算法,我们使用ConcurrentHashMap做key-value结果存储元素的映射关系,使用ConcurrentLinkedDeque来维持key的访问顺序。

LRU实现代码:

packagecom.lizba.redis.lru;importjava.util.Arrays;importjava.util.List;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.ConcurrentLinkedDeque;/***<p>*LRU简单实现*</p>**@Author:Liziba*@Date:2021/9/1723:47*/publicclassSimpleLru{/**数据缓存*/privateConcurrentHashMap<String,Object>cacheData;/**访问顺序记录*/privateConcurrentLinkedDeque<String>sequence;/**缓存容量*/privateintcapacity;publicSimpleLru(intcapacity){this.capacity=capacity;cacheData=newConcurrentHashMap(capacity);sequence=newConcurrentLinkedDeque();}/***设置值**@paramkey*@paramvalue*@return*/publicObjectsetValue(Stringkey,Objectvalue){//判断是否需要进行LRU淘汰this.maxMemoryHandle();//包含则移除元素,新访问的元素一直保存在队列最前面if(sequence.contains(key)){sequence.remove();}sequence.addFirst(key);cacheData.put(key,value);returnvalue;}/***达到最大内存,淘汰最近最少使用的key*/privatevoidmaxMemoryHandle(){while(sequence.size()>=capacity){StringlruKey=sequence.removeLast();cacheData.remove(lruKey);System.out.println("key:"+lruKey+"被淘汰!");}}/***获取访问LRU顺序**@return*/publicList<String>getAll(){returnArrays.asList(sequence.toArray(newString[]{}));}}

测试代码:

packagecom.lizba.redis.lru;/***<p>*测试最近最少使用*</p>**@Author:Liziba*@Date:2021/9/180:00*/publicclassTestSimpleLru{publicstaticvoidmain(String[]args){SimpleLrulru=newSimpleLru(8);for(inti=0;i<10;i++){lru.setValue(i+"",i);}System.out.println(lru.getAll());}}

测试结果:

从上数的测试结果可以看出,先加入的key0,key1被淘汰了,最后加入的key也是最新的key保存在sequence的队头。

通过这种方案,可以很简单的实现LRU算法;但缺点也十分明显,方案需要使用额外的数据结构来保存key的访问顺序,这样会使Redis内存消耗增加,本身用来优化内存的方案,却要消耗不少内存,显然是不行的。

5、Redis的近似LRU

针对这种情况,Redis使用了近似LRU算法,并不是完完全全准确的淘汰掉最近最不经常使用的key,但是总体的准确度也可以得到保证。

近似LRU算法非常简单,在Redis的key对象中,增加24bit用于存储最近一次访问的系统时间戳,当客户端对Redis服务端发送key的写入相关请求时,发现内存达到maxmemory,此时触发惰性删除;Redis服务通过随机采样,选择5个满足条件的key(注意这个随机采样allkeys-lru是从所有的key中随机采样,volatile-lru是从设置了过期时间的所有key中随机采样),通过key对象中记录的最近访问时间戳进行比较,淘汰掉这5个key中最旧的key;如果内存仍然不够,就继续重复这个步骤。

注意,5是Redis默认的随机采样数值大小,它可以通过redis.conf中的maxmemory_samples进行配置:

针对上述的随机LRU算法,Redis官方给出了一张测试准确性的数据图:

最上层浅灰色表示被淘汰的key,图一是标准的LRU算法淘汰的示意图

中间深灰色层表示未被淘汰的旧key

最下层浅绿色表示最近被访问的key

在Redis3.0maxmemory_samples设置为10的时候,Redis的近似LRU算法已经非常的接近真实LRU算法了,但是显然maxmemory_samples设置为10比maxmemory_samples设置为5要更加消耗CPU计算时间,因为每次采样的样本数据增大,计算时间也会增加。

Redis3.0的LRU比Redis2.8的LRU算法更加准确,是因为Redis3.0增加了一个与maxmemory_samples相同大小的淘汰池,每次淘汰key的时候,先与淘汰池中等待被淘汰的key进行比较,最后淘汰掉最老旧的key,其实就是被选中淘汰的key放到一起再比较一下,淘汰其中最旧的。

6、存在问题

LRU算法看似比较好用,但是也存在不合理的地方,比如A和B两个key,在发生淘汰时的前一个小时前同一时刻添加到Redis,A在前49分钟被访问了1000次,但是后11分钟没有被访问;B在这一个小时内仅仅第59分钟被访问了1次;此时如果使用LRU算法,如果A、B均被Redis采样选中,A将会被淘汰很显然这个是不合理的。

针对这种情况Redis4.0添加了LFU算法,(Leastfrequentlyused)最不经常使用,这种算法比LRU更加合理,下文将会一起学习中淘汰算法,如有需要请关注我的专栏。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
钢琴块2umod1.7superman下载地址 ...它的肚子大小和喂食有什么关系,另外,它蜕皮的先兆是什么啊 男神今天过生日 零点发了一个生日礼物的图片 配字是咳咳。大家觉得这个... 科研绘图工具Graphpad Prism 8升级到Graphpad Prism 9了,更快做出SCI... GraphPad Prism 软件介绍 SCI写作-用TCGA数据库和Graphpad Prism绘制KM生存曲线Kaplan-Meier c... GraphPad Prism 简单易学,和Excel一样傻瓜,但比Excel规范得多(免费学... 外国语言文学包括哪些专业 四川大学外国语学院跟四川大学或四川外国语学院有什么关系呀_百度知 ... 2024四川大学考研专业有哪些 怎么在异地恋中做一个懂事的女朋友? Redis集群攻略 - 这5张图让你秒懂分布式缓存背后原理 ...是异地恋,怎么给于她更多的安全感?我应该怎么做。麻烦各位有经验的哥... 橙子子怎么种植 realmegtneo2怎么打开nfc PS磁性套索工具怎么取消一个点ps中磁性套索工具怎么取消 苹果11总成怎么区分原装和组装 磁性套索工具无法结束 女生进)如果我变成一只你讨厌的虫子,你会踩死我吗?你会穿什么鞋踩死我... ...我变成小虫子,爬到你脚边,被你发现,你会踩死我吗?会怎么踩?_百度知 ... ...不喜欢小虫子 跑到了你们脚边 你们会踩死我吗?会怎麼踩? cost怎么读什么意思 《动物森友会》跑步方法介绍介绍_《动物森友会》跑步方法介绍是什么_百 ... 动物之森布料怎么用介绍_动物之森布料怎么用是什么 新n是新疆哪里的车牌 excel身份证号码最后一位为什么是000 如何在Excel中显示数字零? excel中怎么显示身份证后面的数字。 阴阳家语录全文精选,阴阳家语录精选 苹果6屏幕有时候点不动。上面有阴影。敲下又可以?怎么回事 Redis集群详述(从服务内部讲解,彻底搞懂Redis集群) 换手机号码会影响抖音吗? soul被禁止私聊原因 soul怎么不能私聊了呢? soul为什么不能和对方对话了呢? 为什么soul无法私信 soul私聊被禁止怎么解决? ?核酸检测阴性但还是黄码能出门吗 核酸检测还没到期可以再做第二次吗... 为什么我的蓝牙耳机连接到手机上,手机有声音? 肺炎了,还有救吗? 定向免流卡是什么卡 现在肺炎越来越越多有救吗我害怕极了? 我老爸现在得了重症肺炎!医生,还可以救吗?怎么救啊? 间质性肺炎还有救吗?2年了 因为肺炎,老人家认为没救了,现在没有活下去的信心怎么办? 胃肠间质瘤的症状 我女儿在桂林人民医院说是肺炎,不能救了,要求我们出院现在我该去哪里... 胃肠道间质瘤是良性吗 有什么方法能把电脑里面所有word文档搜出来?不要其他格式的 ...然后上完了1快。还可以一直上。老板能查到?