Redis的内存策略

news2024/12/26 11:47:54

过期Key处理:

1)Redis之所以性能强大,最主要的原因就是基于内存来存储,然而单节点的Redis内存不宜设置的过大,否则会影响持久化或者是主从复制的性能,可以通过修改配置文件来设置redis的最大内存,通过maxmemory 1gb,但是当内存达到上限的时候,就无法存储更多数据了

2)在之前学习Redis缓存的时候,可以通过expire命令来给Redis的key设置TTL过期时间

3)可以发现,当key的ttl过期之后,再一次访问name的时候返回的是nul,说明这个key已经不存在了,对应的内存也就释放了,从而起到了内存回收的目的

4)Redis本身是一个典型的key-value键值对的内存存储的数据库,因此所有的key和value都是保存在之前学习过的Dict结构中,但是在database结构体中有两个Dict,一个是用来记录key-value,一个用来记录key-ttl;

 

在设置键的过期时间的同时,设置一个定时器,当键过期了,定时器马上把该键删除

定时删除对内存来说是友好的,因为它可以及时清理过期键,但对CPU是不友好的,如果过期键太多,删除操作会消耗过多的资源

在过期 key 比较多的情况下,删除过期 key 这一行为可能会占用相当一部分 CPU 时间,在内存不紧张但是 CPU 时间非常紧张的情况下,将 CPU 时间用在删除和当前任务无关的过期 key 上,无疑会对服务器的响应时间和吞吐量造成影响

Redis采用的是 惰性删除 + 定期删除 的策略

一)惰性删除:顾名思义就是说并不是在TTL到期之后就立即删除,而是在访问一个Key的时候(增删改查),检查该Key的存活时间,如果过期了才执行删除;

当查询数据的时候,首先根据key查询对应的expires,检查是否过期,如果过期就返回null,并删除对应的数据和expires,如果不过期,就返回数据

假设给一个key设置了过期时间,但是永远不会访问这个key,那么这个key就永远也不会被删除,这样就会导致内存占用过多的状况

这种方式看似很完美,在访问的时候检查key的过期时间,不会占用太多的额外CPU资源但是如果一个key已经过期了,如果长时间没有被访问,那么这个key就会一直存留在内存之中,严重消耗了内存资源,并且删除的目标仅限于当前处理的 key不会在删除其他无关的过期 key 上花费任何 CPU 时间

二)周期删除:顾名思义就是设置一个定时任务,周期性的抽样部分过期的key,然后执行删除,执行周期有两种:

定期删除是定时删除和惰性删除的一个折中方案,每隔一段时间来删除过期键,可以根据实际场景自定义这个间隔时间,在CPU资源和内存资源上作出权衡

2.1)Redis初始化会设置一个定时任务serverCron(),按照server.hz(1s中最多执行1次)的频率来执行过期的key的清理,模式为slow,过期的key,早晚会被抽到,执行时间长,执行的频率比较低;

2.2)Redis初始化后的1ms去执行

2.3)serverCron的返回值是完成serverCron再隔多少毫秒之后执行此定时任务(100ms)

2.4)getLRUClock时钟是更新lruclock到当前时间,为后期的LRU和LFU做准备,并且接下来会调用atomicSet方法设置到server.lruclock里面,lruclock是redis内部维护的一个时钟,是以微秒为单位的,每隔一个周期会进行记录一次,每隔100ms会更新一次,每调用一次serverCron方法lruclock是不断的会发生变化的

2.5)开始执行数据库的数据清理,例如说过期的key的清理;

2.2)Redis的每一个事件循环前会调用beforeSleep()函数,执行过期的key清理,模式为FAST,执行频率比较高,执行的时间非常短,不超过1ms

1)redis服务一进行启动,先调用initserver()函数,完成各种各样的注册,创建ServerSocket以及eventpoll,将ServerSocket对应的fd挂到红黑树上面;

2)接下来会执行aemain来进行事件循环,开启一个事件循环,会不断的调用beforesleep,再来调用aeapipoll,相当于是epoll_wait,等待事件就绪,如果事件就绪了,就进行对应的Socket的读写,处理IO事件;

3)最后会调用serverCron()进行定时任务的清理,但是除了redis在初始化之后1ms调用serverCron之外,以后的serverCron执行完成之后都会返回一个1000/server.sz,返回的就是100ms,默认就是下一次aeApiPoll执行的时间,所以当while循环执行的时候程序会进行检查serverCron()执行的时间到了没有,如果时间到了才执行,确保每隔100ms执行一次,时间不到就不执行serverCron函数了,因为while循环速度非常快,如果在每一次循环中都去调用serverCron()函数,那么这个函数执行的频率就会非常高了,但是再beforeSleep模式中,Fast模式清理是随着循环而进行的,每一次执行while循环,都会执行fast回收;

4)因为fast执行的模式是非常快的,清理速度也是非常快的,一次执行的速度会控制在1ms以内,而slow模式可能执行一次可能是消耗几十毫秒,如果serverCron()每循环一次都要执行,每一次主线程执行while循环都需要卡顿几十毫秒,那么主线程执行性能将会非常低,所以slow模式并不是在每一次循环都执行,而是每隔100ms之后执行,避免主线程阻塞;

slow模式属于是低频,长时间的清理,清理效果会更好一点,可以清理更多的Key

fast模式属于高频,少量清理,耗时时间非常的短,最长不超过1ms

但是他们都是为了在不阻塞主线程的情况下,尽可能过多的清理过期的key

slow模式规则:低频高时长的执行

1)执行频率受server.hz影响,默认是10,即每一秒执行10次,每一个执行周期是100ms

2)执行清理key的耗时不超过一次执行周期的25%;

3)逐个遍历db,逐个遍历db中的bucket,抽取20个key查看他们是否过期,直到把所有的buket都遍历到,如果发现过期的key就直接干掉;

4)如果没有达到时间上线25ms况且过期key的比例超过10%,那么再次进行一次抽样,否则结束;

fast模式规则:过期key的比例小于10%不执行,高频低时长

1)执行频率受beforesleep()函数调用的影响,但是两次fast模式执行的间隔不应该低于2ms,fast模式每一次执行前都会判断上一次fast到现在的时间,如果不足2ms就会跳过;

2)执行清理的耗时不超过1ms

3)遍历整个db,逐个遍历db中的bucket,抽取20个key判断是否过期

4)如果没有达到时间上限1ms,况且过期key的比例超过10%,那么在进行一次抽样否则结束

Redis 中,flushall 和 flushdb 都是清空当前数据库的操作,但是两者有很大的区别:

1)flushall 清空数据库并执行持久化操作,也就是 rdb 文件会发生改变,变成 76 个字节大小(初始状态下为 76 字节),所以执行 flushall 之后数据库真正意义上清空了
2)flushdb 清空数据库,但是不执行持久化操作,也就是说 rdb 文件不发生改变。而 Redis 的数据是从 rdb 快照文件中读取加载到内存的,所以在 flushdb 之后,如果想恢复数据库,则可以直接 kill 掉 redis-server 进程,然后重新启动服务,如此 Redis 重新读取 rdb 文件,数据恢复到 flushdb 操作之前的状态;
3)注意:要直接 kill 掉 redis-server 服务,因为 shutdown 操作会触发持久化,lsof -i:6379命令查看 redis-server 的进程号,然后 kill 即可

redis的内存淘汰策略:

1)redis的过期策略可以将redis中一些过期的key直接删除,但是在一些庞大的项目中,因为数据量非常的多,请求不断地向redis中进行存储数据,很有可能仅仅淘汰过期的key也很难满足内存的使用,此时内存也有可能达到上限;

2)内存淘汰:就是当Redis的内存使用达到设定的阈值的时候,Redis主动挑选部分key删除以释放更多的内存的流程

redis在每一次执行命令的时候,都会进行检查,看看服务器内存是否够用

实际上,Redis支持8种不同的策略来选择要删除的key:

1)noeviction:不淘汰任何key,但是内存满的时候不允许写入任何新数据,默认就是这种策略

2)volatile-ttl:对设置了TTL的key,比较key的剩余ttl的值,ttl越小越先进行淘汰

3)allkeys-random:对于全体key,随机来进行淘汰,也就是从db->dict中进行挑选

4)volatile-random:对设置了ttl中的key随机进行淘汰,也就是从db->expires中进行挑选

LRU:Least Recently Used,最少最近使用,用当前时间减去最后一次访问时间(就是你多久没有被访问了),这个值越大淘汰的优先级就会越高,代表很长时间没有被访问了

LFU:Least Frequently Used,最少频率使用,会统计每一个key的使用频率,值越小淘汰率优先级越高

那么Redis是如何统计最近访问的时间和最近访问的频率呢?

Redis中所有存储的键值对最后都被封装成了RedisObject

1)当配置文件中指定的内存淘汰策略不同,实际上lru中记录的值也是不同的

Lru的访问次数之所以叫逻辑访问次数是因为并不是每一次key访问都会被计数而是用过运算:

2)但是第一次访问的时候P计数器一定是+1的,后续的访问计数器的次数不一定会累加,完全取决于概率,对这个key访问的次数越多,分母的值就越大,P的值就越小,R<P的概率也越小,计数器累加的概率也是越小的;

3)访问次数衰减,以当前时间为单位,减去上一次访问的时间,上一次访问的时间再LFU的高16位中存着呢;

 

1)首先会进行判断内存是否充足,就是已经使用的内存是否已经超过内存使用的最大值,如果内存充足就直接返回了;

2)如果内存不充足,redis会进行判断内存淘汰策略是否是noeviction,客户端就会报错;

3)接下来Redis会进行判断内存淘汰策略是根据所有key来进行淘汰还是根据过期的key进行淘汰,判断是否是AllKeys;

4)接下来redis会进行判断内存淘汰策略,如果是Random策略,就根据第三步选择的是在所有key中进行淘汰还是在过期key中进行淘汰,遍历DB随机选取一个key进行淘汰;

5)每一次删除完成之后都会去进行判断已经释放的内存是否满足于内存的需要,如果不满足就直接返回第四步,进行判断内存淘汰策略,尝试进行删除key;

6)如果在第三步中,内存淘汰策略不是随机的,那么有可能是ttl越小越先进行淘汰,LRU和LFU,都要判断TTL的值,进行比较访问次数/TTL值/上一次访问的时间,但是不能一个一个的key进行比较,如果通过遍历的方式进行比较,那么会导致此次内存淘汰的性能将会变得非常低,为了解决这个问题,Redis内部内置了一个eviction_pool,这个池子将来会对存放到里面的key中的某一个值做升序排列,将来值越大的越先淘汰,这个值是idleTime

7)Redis会先获取到Redis中随机的一个数据库,拿到这个数据库以后,然后随机进行挑选5个key,但是这5个key不一定都会被扔到eviction_pool池子里面,还要从这5个key中做筛选,接下来就通过不同的策略来执行筛选的标准;

7.1)对于volatile-ttl来说,使用maxTTL-TTL作为idleTime;

7.2)对于LRU来说,使用now-LRU值作为idleTime

7.3)对于LFU来说,使用255-LRU值作为idleTime

8)然后判断这个key是否应该存放到eviction_pool里面,如果池子已经满了,那么Redis会进行判断当前key的idleTime和池子中的一个key的idle值要大,如果要存放的key的idle值要比池子中的idle值大,那么就将这个key存放进去,并删除idle最小的key

9)接下来再来循环处理下一个数据库,重复第七步

10)接下来倒序从池子里面获取一个key进行删除,判断删除完成之后内存是否充足,如果不充足,返回第三步;

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

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

相关文章

javaScript蓝桥杯-----粒粒皆辛苦

目录 一、介绍二、准备三、目标四、代码五、完成 一、介绍 俗话说“民以食为天”&#xff0c;粮食的收成直接影响着民生问题&#xff0c;通过对农作物产量的统计数据也能分析出诸多实际问题。 接下来就让我们使用 ECharts 图表&#xff0c;完成 X 市近五年来的农作物产量的统…

Python批量下载参考文献|基于Python的Sci-Hub下载脚本|Python批量下载sci-hub文献|如何使用sci-hub批量下载论文

本篇博文将介绍如何通过Python的代码实现快速下载指定DOI号对应的文献&#xff0c;并且使用Sci-Hub作为下载库。 一、库函数准备 在开始之前&#xff0c;我们需要先安装一些必要的库&#xff0c;包括&#xff1a; requests&#xff1a;发送HTTP请求并获取响应的库&#xff1…

南山城市更新--向南村(一期,二期)项目详情

向南村&#xff08;一期&#xff09;城市更新单元项目简介 项目于2010年被列入《深圳城市更新单元规划制定计划第一批计划》中&#xff0c;申报主体为向南实业股份有限公司&#xff0c;后与恒大合作开发。 项目位于南山区桂庙路南侧&#xff0c;毗邻前海、衔接后海&am…

经典算法:Fenwick Tree

经典算法&#xff1a;Fenwick Tree 1. 算法简介2. 原理介绍3. 算法实现4. 例题说明 1. 解题思路2. 代码实现 5. 参考链接 1. 算法简介 Fenwick Tree又称为Binary Indexed Tree&#xff0c;也算是一种常见的数据结构了。 他其实某种意义上来说算是Segment Tree的一种变体&…

克隆虚拟机

上一篇我们已经讲过了启动虚拟机并安装Linux系统&#xff0c;下面我们来讲一下如何通过已经创建好的虚拟机spark01克隆出spark02和spark03来&#xff0c;从而满足搭建大数据集群环境需要多台虚拟机的需求。 首先我们要理解两个概念&#xff1a; 1.完整克隆 完整克隆的虚拟机可…

【算法证明 三】计算顺序统计量的复杂度

计算顺序统计量&#xff0c;在 c 标准库中对应有一个函数&#xff1a;nth_element。其作用是求解一个数组中第 k 大的数字。常见的算法是基于 partition 的分治算法。不难证明这种算法的最坏复杂度是 Θ ( n 2 ) \Theta(n^2) Θ(n2)。但是其期望复杂度是 Θ ( n ) \Theta(n) …

从源码全面解析 dubbo 服务暴露的来龙去脉

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、Spring源码系列、Netty源码系列、Kafka源码系列、JUC源码…

SpringBoot配置 -- SpringBoot快速入门保姆级教程(二)

文章目录 前言二、SpringBoot配置1. 了解配置文件的3种格式2.yaml格式语法规则3.读取yaml数据的3种方式4.多环境开发配置5.多环境命令行启动参数设置6. 多环境开发兼容问题7.配置文件分类 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&am…

vcruntime140.dll如何修复

VCRUNTIME140.dll是Windows操作系统上一个非常重要的动态链接库文件&#xff0c;它是由Microsoft Visual C Runtime提供的运行时库文件之一&#xff0c;被许多应用程序用来进行编译和运行。如果该文件丢失或损坏&#xff0c;很多应用程序就无法正常运行&#xff0c;这可能会带来…

三分钟了解SpringBoot配置优先级底层源码解析

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是冰点&#xff0c;从业11年&#xff0c;目前在物流独角兽企业从事技术方面工作&#xff0c;&#x1f342;博主正在努力完成2023计划中&#xff1a;以梦为马&#xff0c;扬帆起航&#xff0c;2023追梦人&#x1f4dd;联系…

关于性能测试平台的一些想法,想跟大家聊一下

目录 一、任务管理 二、用例管理 三、环境管理 四、压测机管理 五、数据管理 六、监控管理 七、日志管理 八、报表管理 九、配置管理 十、系统管理 组织架构 这里我按照每个不同系统归属的项目组为横向&#xff0c;性能测试团队作为职能部门为纵向的矩阵式组织架构为…

JUC学习(二)

目录 Doug Lea — JUC并发包的作者锁框架Lock和Condition接口可重入锁公平锁与非公平锁读写锁锁降级和锁升级队列同步器AQS底层实现公平锁一定公平吗&#xff1f;Condition实现原理 ——————————————————————————————— 在前面&#xff0c;我们了解…

ICV报告:乘光伏新能源汽车之势,功率器件蓄势待发

前言&#xff1a; 电力电子器件&#xff08;Power Electronic Device&#xff09;&#xff0c;又称为功率半导体器件&#xff0c;用于电能变换和电能控制电路中的大功率(通常指电流为数十至数千安&#xff0c;电压为数百伏以上)电子器件。功率器件能够承受和控制较大电流、电压…

无限阳光、自动收集阳光CALL、阳光产生速度

简单实现无限阳光 本次实验内容&#xff1a;通过逆向分析植物阳光数量的动态地址找到阳光的基址与偏移&#xff0c;从而实现每次启动游戏都能够使用基址加偏移的方式定位阳光数据&#xff0c;最后我们将通过使用C语言编写通用辅助实现简单的无限阳光外挂&#xff0c;在教程开始…

Vue Router路由管理器

目录&#xff1a; 相关理解基本路由几个注意事项嵌套&#xff08;多级&#xff09;路由路由的query参数命名路由路由的params参数路由的props配置路由跳转的replace方法编程式路由导航缓存路由组件activated和deactivated路由守卫路由器的两种工作模式 相关理解 vue-route…

博学谷学习记录】超强总结,用心分享 | 架构师 敏捷开发 学习总结

文章目录 敏捷开发1. 概述2. 敏捷开发 敏捷开发 1. 概述 随着软件开发技术的不断发展&#xff0c;现在出现了很多种不同的开发模式&#xff0c;其实敏捷开发已经成为现在很多企业开发应用程序都想要选择的开发方案&#xff0c;那么什么是敏捷开发呢&#xff1f;1.1 四种开发模…

Linux 配置Java环境(一)

Linux 配置Java环境 一、配置Java环境1、查看系统是否有java环境2、卸载系统自带的jdk3、创建一个文件夹用于存放java的压缩包4、包下载好的jdk拖到java文件夹5、安装jdk6、配置环境变量7、让配置生效8、验证是否配置成功 一、配置Java环境 1、查看系统是否有java环境 输入指…

nginx中location和rewrite

常用的Nginx 正则表达式 ^ &#xff1a;匹配输入字符串的起始位置 $ &#xff1a;匹配输入字符串的结束位置 * &#xff1a;匹配前面的字符零次或多次。如“ol*”能匹配“o”及“ol”、“oll” &#xff1a;匹配前面的字符一次或多次。如“ol”能匹配“ol”及“oll”、“olll…

0-1背包问题:动态规划的经典应用

文章目录 引言背包问题简介0-1背包问题定义0-1背包问题的限制条件 动态规划解决思路状态定义状态转移方程 背包问题的Java实现示例与分析 总结 引言 背包问题是在给定一组物品和一个背包容量的情况下&#xff0c;如何选择物品放入背包&#xff0c;以使得放入背包的物品总价值最…

高边功率开关参数Load current(ISO)和Nominal current

1. IL(nom)是没有加散热片的情况下&#xff0c;考虑RON和BTS6133D与环境热阻计算得到的电流值&#xff1b; 2. IL(iso)是有散热片的情况下计算得到的电流值&#xff1b; 3. IL12(SC)是如果负载电流达到75A以上&#xff0c;BTS6143D会通过不断重启来来限制电流在75A以下。