Redis 内存淘汰和过期删除策略

news/2024/5/6 13:28:42/文章来源:https://blog.csdn.net/airingyuan/article/details/128116699

提起使用Redis的优点,大家可以列举出许多,比如:数据存储在内存,读写速度快,性能优异。比如数据持久化,便于数据备份及恢复等等。

分布式服务系统平台发展至今,Redis活跃在平台的各个领域,比如日志系统、秒杀、限时活动等等。我们通常会将Redis的数据分为永久及过期删除数据。那么到过期时间后,数据是通过什么策略删除的呢?Redis又提供了哪些过期删除策略?

提到数据的过期删除,又不得不让我们联想到Redis的内存淘汰机制,两者都是删除数据,又有什么不同之处呢?

带着这些问题,我们来一起探究下Redis的过期删除与内存淘汰机制。

  • 过期删除

1.Redis Expires  Dict及删除逻辑源码解读

Redis可以对key设置过期时间,每当我们对一个key设置了过期时间时,Redis会把该key带上过期时间存储到一个过期字典中。过期字典是存储在redisDb这个结构里的,过期字典的键指向Redis数据库中的某个key,过期字典的值是一个long long类型的整数,这个整数保存了key所指向的数据库键的过期时间(毫秒精度的UNIX时间戳)。结构如下图所示:

Redis 在访问或者修改 key 之前,都会调用 expireIfNeeded 函数对其进行检查,检查 key 是否过期:

  • 如果过期,则删除该 key,至于选择异步删除,还是选择同步删除,根据lazyfree_lazy_expire 参数配置决定(Redis 4.0版本开始提供参数),然后返回 null 给客服端;
  • 如果没有过期,不做任何处理,然后返回正常的键值对给客户端;

过期判断逻辑:

删除逻辑的底层代码如下:

2.过期删除策略

Redis的数据删除有定时删除(立即删除)、惰性删除和定期删除三种方式。

定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

上面介绍了三种过期删除策略,每一种都有优缺点,仅使用某一个策略都不能满足实际需求,所以Redis选择惰性删除+定期删除这两种策略配合使用,以求在合力使用CPU时间和避免内存浪费之间取得平衡。

定时删除与惰性删除这两种策略大家都好理解,那么Redis是怎么实现定期删除的呢?

在Redis中,默认每秒进行10次过期检查,此配置可以通过Redis的配置文件redis.conf进行配置。配置redis.conf的hz选项默认为10(即1秒执行10次,100ms一次,值越大说明刷新频率越快,对redis性能损耗也越大)。

过期键的定期删除策略由 redis.c/activeExpireCycle 函数实现,调用流程为 serverCron() -> databasesCron() -> activeExpireCycle()。核心代码如下(为了方便查看核心部分,对代码进行了截取):

      这里有两个问题,

      问题1:定期删除时候,每次随机抽取的数量是多少?

       这个是由上图activeExpireCycle函数的ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP定义的,这个值是写死在程序中的,值为20。也就是说,每轮抽查时,会随机选择20key判断是否过期。

问题2:定期删除是一个循环流程,redis如何保证不会出现循环过度,导致线程卡死现象?

为此,增加了定时删除循环流程的时间上限,默认不超过25ms

问题3 redis是单线程的,线程不仅处理定时任务,还处理客户端请求。单线程redis,如何知道要执行定时任务呢?

Redis 的定时任务会记录在一个称为最小堆的数据结构中。这个堆中,最快要执行的任务排在堆的最上方。在每个循环周期,Redis 都会将最小堆里面已经到点的任务立即进行处理。处理完毕后,将最快要执行的任务还需要的时间记录下来,这个时间就是接下来处理客户端请求的最大时长,若达到了该时长,则暂时不处理客户端请求而去运行定时任务。

  • 内存淘汰策略

前面部分我们讨论的是删除已过期的key,而当redis的运行内存达到了我们设置的最大运行内存时,才会触发内存淘汰策略。

32位和64位操作系统,maxmemory的默认值是不同的。

64位操作系统:maxmemory的默认值是0,表示没有内存大小限制,那么不管用户存放多少数据到reidsredis也不会对可用内存进行检查,直到redis实例因内存不足而崩溃为止。

32位操作系统:maxmemory的默认值是3G,因为32位的机器最大只支持4GB的内存,而系统本身就需要一定的内存资源来支持运行,所以32位操作系统限制最大3GB的可用内存是非常合理的,这样可以避免因为内存不足而导致Redis实例崩溃。

4.0 版本之前 Redis 的内存淘汰策略有以下 6 种。

策略名称

说明

noeviction

不淘汰任何数据,当内存不足时,执行缓存新增操作会报错,它是 Redis 默认内存淘汰策略

allkeys-lru

淘汰整个键值中最久未使用的键值

allkeys-random

随机淘汰任意键值

volatile-lru

淘汰所有设置了过期时间的键值中最久未使用的键值

volatile-random

随机淘汰设置了过期时间的任意键值

volatile-ttl

优先淘汰更早过期的键值

而在 Redis 4.0 版本中又新增了 2 种淘汰策略:

策略名称

说明

volatile-lfu

淘汰所有设置了过期时间的键值中最少使用的键值

allkeys-lfu

淘汰整个键值中最少使用的键值

内存淘汰策略我们可以通过配置文件来修改,redis.conf 对应的配置项是“maxmemory-policy noeviction”,只需要把它修改成我们需要设置的类型即可。

需要注意的是,如果使用修改 redis.conf 的方式,当设置完成之后需要重启 Redis 服务器才能生效。还有另一种简单的修改内存淘汰策略的方式,我们可以使用命令行工具输入“config set maxmemory-policy noeviction”来修改内存淘汰的策略,这种修改方式的好处是执行成功之后就会生效,无需重启 Redis 服务器。但它的坏处是不能持久化内存淘汰策略,每次重启 Redis 服务器之后设置的内存淘汰策略就会丢失。

LRU 算法和 LFU 算法

  1. LRU 算法

LRU 全称是 Least Recently Used 翻译为最近最少使用,会选择淘汰最近最少使用的数据。

传统 LRU 算法的实现是基于「链表」结构,链表中的元素按照操作顺序从前往后排列,最新操作的键会被移动到表头,当需要内存淘汰时,只需要删除链表尾部的元素即可,因为链表尾部的元素就代表最久未被使用的元素。

Redis 并没有使用这样的方式实现 LRU 算法,因为传统的 LRU 算法存在两个问题:

  • 需要用链表管理所有的缓存数据,这会带来额外的空间开销;
  • 当有数据被访问时,需要在链表上把该数据移动到头端,如果有大量数据被访问,就会带来很多链表移动操作,会很耗时,进而会降低 Redis 缓存性能。

Redis 是如何实现 LRU 算法的?

Redis 实现的是一种近似 LRU 算法,目的是为了更好的节约内存,它的实现方式是在 Redis 的对象结构体中添加一个额外的字段,用于记录此数据的最后一次访问时间。

Redis 进行内存淘汰时,会使用随机采样的方式来淘汰数据,它是随机取 5 个值(此值可配置),然后淘汰最久没有使用的那个。

Redis 实现的 LRU 算法的优点:

  • 不用为所有的数据维护一个大链表,节省了空间占用;
  • 不用在每次数据访问时都移动链表项,提升了缓存的性能;

但是 LRU 算法有一个问题,无法解决缓存污染问题,比如应用一次读取了大量的数据,而这些数据只会被读取这一次,那么这些数据会留存在 Redis 缓存中很长一段时间,造成缓存污染。

因此,在 Redis 4.0 之后引入了 LFU 算法来解决这个问题。

  1. LFU 算法

LFU 全称是 Least Frequently Used 翻译为最近最不常用的,LFU 算法是根据数据访问次数来淘汰数据的,它的核心思想是如果数据过去被访问多次,那么将来被访问的频率也更高

所以, LFU 算法会记录每个数据的访问次数。当一个数据被再次访问时,就会增加该数据的访问次数。这样就解决了偶尔被访问一次之后,数据留存在缓存中很长一段时间的问题,相比于 LRU 算法也更合理一些。

  • 总结      

Redis使用惰性删除+定期删除来管理已过期的key。而内存淘汰策略是为了解决内存过大问题,当redis的运行内存超过最大运行内存时,就会触发内存淘汰策略。

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

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

相关文章

RabbitMQ事务消息

通过对信道的设置实现 channel.txSelect();通知服务器开启事务模式;服务端会返回Tx.Select-Ok channel.basicPublish;发送消息,可以是多条,可以是消费消息提交ackchannel.txCommit() ;提交事务;…

mmdetection3d SUN RGB-D数据集预处理

SUN RGB-D是普林斯顿大学发布的一种关于室内场景理解的数据集,共包含了10335个样本,其中训练样本和验证测试样本数量分别为5285和5050。每个样本包含了彩色图像(RGB)和深度(D)信息,并且分别进行…

基于BDD的接口自动化框架开箱即用

1、背景说明 项目思想:BDD 行为驱动开发的思想褒贬不一,这里不多说。遵循的宗旨能解决业务痛点的思想就是好思想。 接口测试工具在实际的业务测试场景中往往会遇到一些使用上的局限性,自定义扩展要求技术较高,如果二次开发工具…

小程序瀑布流实现

什么是瀑布流布局 瀑布流布局,一般等宽,不等高的列表排列 原理是找出高度之和最小的那一列,在高度最小列继续添加元素 可以通过 absolute 定位实现,动态计算每一项的 top 和 left 封装瀑布流方法 function getAllRect(context…

[附源码]Python计算机毕业设计Django的疫苗接种管理系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

c#、wpf开发中页面在win10下被缩放125%引起页面错乱的解决办法。

正常情况下,我们开发的页面页面应该是100%缩放的,这样程序在win7和win10下保持一致,但是win10里面会根据显示器的情况自动调整“缩放与布局”,这使得桌面程序有时候会发生页面错乱,怎么调整就是个问题。 如图:在“缩放与布局”100%显示如下: 而在 “缩放与布局”125%显…

基于AD Event日志检测LSASS凭证窃取攻击

01、简介 简单介绍一下,LSASS(本地安全机构子系统服务)在本地或域中登录Windows时,用户生成的各种凭证将会存储在LSASS进程的内存中,以便用户不必每次访问系统时重新登录。 攻击者在获得起始攻击点后,需要获取目标主机上的相关凭证…

小程序中的confirm-type设置键盘的确认按钮

详情: confirm-type是很多小程序组件中的一种设置,用于改变输入键盘右下角的确认按钮。比如说,正常情况下,键盘上的默认提示可能是完成,但是你可以通过confirm-type将其设置为发送,搜索等,在特…

搬砖日记:关于sync用不了的问题

自己封装了个输入框的组件&#xff0c;想要实现的输入框的值的修改可以实时修改到父组件的值 印象中看到过人家用.sync修饰符去实现这个功能&#xff0c;大抵是 //父组件 <searchInput :value.sync"value"></searchInput> //子组件 <input v-model&qu…

Redeis缓存查询基于元注解与AOP结合使用——不过时的优雅

Redeis缓存查询基于元注解与AOP结合使用 根据优化需要&#xff0c;数据查询的时候无法避免的使用Redis基于缓存查询&#xff0c;进而减少对于数据库的查询压力&#xff0c;对于过多的方法基于缓存存储&#xff0c;为提高代码的复用性&#xff0c;采用一种不过时的写法。 整体的…

一文详解,数据仓库、数据库、数据中台、数据湖的区别

数据时代&#xff0c;各行业的企业都已经开始通过数据库来沉淀数据&#xff0c;但是真的论起数据库、数据仓库、数据中台&#xff0c;还是新出现的数据湖&#xff0c;它们的概念和区别&#xff0c;可能知道的人就比较少了&#xff0c;今天我们详细来比较了解一下。 数据仓库是…

你的数据库到底应该如何存储密码?

最近接手公司一个之前的服务&#xff0c;竟然发现用户密码是明文存储在数据库中&#xff01; 说实话还是有点吃惊的&#xff0c;这可不兴学 CSDN 呀&#xff08;手动狗头&#xff09;&#xff0c;至少也得搞个 MD5 存一存吧。 不过 MD5 其实也没啥用&#xff0c;今天我们就来…

JVM之运行时数据区 PC、虚拟机栈、本地方法栈

JVM之运行时数据区 PC、虚拟机栈、本地方法栈PC寄存器线程回顾寄存器实践面试使用PC寄存器存储字节码指令地址有什么用&#xff1f;为什么使用PC寄存器记录当前线程的执行地址PC寄存器为什么会被设定为线程私有虚拟机栈虚拟机栈出现背景简介栈可能出现的异常栈中存储着什么运行…

电商新趋势:Starday拿下黑色星期五的制胜法宝是物流速度

国内电商“双十一”购物狂欢季活动已经闭幕&#xff0c;“双十二”又将袭来&#xff0c;但更多人却将眼光放在蓬勃发展的跨境电商行业中。当下跨境电商卖家们正在各大跨境电商服务平台的带领下全力备战&#xff0c;在“黑色星期五”期间推出各类大促活动&#xff0c;奋力冲刺20…

Unknown custom element: <el-image>无法使用该组件,升级element-ui版本后项目报错

需求背景&#xff1a; 项目中需要使用图片点击放大&#xff0c;想要使用<el-image>组件&#xff0c;引入后报了下面的错&#xff0c;需要升级element版本&#xff0c;element-ui版本过低&#xff0c;没有该组件。 过程&#xff1a; cnpm i element-ui2.14.1 --save-dev…

clickhouse远程访问Oracle 11g数据库(clickhouse-jdbc-bridge)

1、简介 clickhouse-jdbc-bridge&#xff1a;是clickhouse提供的一个jdbc组件&#xff0c;用于通过JDBC的方式远程访问其他数据库表。 2、安装 &#xff08;1&#xff09;下载源文件并打包获取clickhouse-jdbc-bridge-2.0.7-shaded.jar 在官网&#xff1a;https://github.c…

tictoc例子理解10-13

tictoc10-13tictoc 10 几个模块连接&#xff0c;发送消息直到模块3收到消息tictoc 11 新增信道定义tictoc 12 双向连接信息简化定义tictoc 10 几个模块连接&#xff0c;发送消息直到模块3收到消息 让我们用几个(n)’ tic’模块让它更有趣&#xff0c;并将每个模块连接到其他模…

ANR 触发、监控、分析 一网打尽

平时看博客或者学知识&#xff0c;学到的东西比较零散&#xff0c;没有独立的知识模块概念&#xff0c;而且学了之后很容易忘。于是我建立了一个自己的笔记仓库 (一个我长期维护的笔记仓库&#xff0c;感兴趣的可以点个star~你的star是我写作的巨大大大大的动力)&#xff0c;将…

大客车玻璃擦净器设计

目 录 摘 要 I ABSTRACT II 1 绪论 1 1.1选题背景及意义 1 1.2发展现状 2 1.3发展趋势 3 1.4研究主要内容 4 2 大客车玻璃擦净器总体方案设计 5 2.1 大客车玻璃擦净器设计思想 5 2.2功能分析 5 2.3工作原理分析 6 2.4功能分解 6 2.4.2传动系统 6 2.4.3真空吸盘 7 2.4.4 清洁刷 …

欢聚季报图解:营收5.87亿美元同比降10% 净利提升

雷递网 雷建平 11月29日欢聚集团(NASDAQ: YY)今日发布2022年第三季度财报。财报显示&#xff0c;欢聚集团2022年第三季度营收为5.867亿美元&#xff0c;较上年同期下降10%。欢聚集团2022年第三季度Bigo Live的平均移动MAU为3540万&#xff0c;较上年同期的3100万增长14.2%&…