【Redis】搞懂过期删除策略和内存淘汰策略

news2025/4/17 6:29:59

1、过期删除策略

1.1、介绍

Redis 是可以对 key 设置过期时间的,因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略。

每当我们对一个 key 设置了过期时间时,Redis 会把该 key 带上过期时间存储到一个过期字典(expires dict)中,也就是说过期字典保存了数据库中所有 key 的过期时间。

字典实际上是哈希表,哈希表的最大好处就是让我们可以用 O(1) 的时间复杂度来快速查找。当我们查询一个 key 时,Redis 首先检查该 key 是否存在于过期字典中:

  • 如果不在,则正常读取键值;
  • 如果存在,则会获取该 key 的过期时间,然后与当前系统时间进行比对,如果比系统时间大,那就没有过期,否则判定该 key 已过期。

1.2、定时删除策略

定时删除策略(TTL)的做法是,在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作。

  • 优点:可以保证过期 key 会被尽快删除,也就是内存可以被尽快地释放。因此,定时删除对内存是最友好的
  • 缺点:在过期 key 比较多的情况下,删除过期 key 可能会占用相当一部分 CPU 时间,在内存不紧张但 CPU 时间紧张的情况下,将 CPU 时间用于删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。所以,定时删除策略对 CPU 不友好

1.3、惰性删除策略

惰性删除策略(Lazy Expire)的做法是,不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。

  • 优点:因为每次访问时,才会检查 key 是否过期,所以此策略只会使用很少的系统资源,因此,惰性删除策略对 CPU 时间最友好;
  • 缺点:如果一个 key 已经过期,而这个 key 又仍然保留在数据库中,那么只要这个过期key 一直没有被访问,它所占用的内存就不会释放,造成了一定的内存空间浪费。所以,惰性删除策略对内存不友好。

1.4、定期删除策略

定期删除策略(Eviction)的做法是,每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。

  • 优点:通过限制删除操作执行的时长和频率,来减少删除操作对 CPU 的影响,同时也能删除一部分过期的数据减少了过期键对空间的无效占用;
  • 缺点:内存清理方面没有定时删除效果好,同时没有惰性删除使用的系统资源少,是一个折中的策略;
  • 缺点:难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好;如果执行的太少,那又和惰性删除一样了,过期 key 占用的内存不会及时得到释放。

1.5、三者区别

1.6、Redis实现

Redis使用的过期删除策略是「惰性删除+定期删除」这两种策略配和使用,以求在合理使用 CPU 时间和避免内存浪费之间取得平衡。

Redis 的惰性删除策略由 db.c 文件中的 expireIfNeeded 函数实现,Redis 在访问或者修改 key 之前,都会调用 expireIfNeeded 函数对其进行检查,检查 key 是否过期:

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

再回忆一下,定期删除策略的做法:每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key

在 Redis 中,默认每秒进行10次过期检查一次数据库,此配置可通过 Redis 的配置文件 redis.conf 进行配置,配置键为 hz,它的默认值是 hz 10

值得注意的是,每次检查数据库并不是遍历过期字典中的所有key,而是从数据库中随机抽取一定数量的 key 进行过期检查。这个一定数量在源码中是写死的,并未提供对应的参数进行自定义配置,数值固定为20

Redis 为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过25ms

1.7、持久化时过期键处理

Redis 持久化文件有两种格式:RDB(Redis Database)和 AOF(Append Only File),下面我们分别来看过期键在这两种格式中的呈现状态。

RDB 文件分为两个阶段,RDB 文件生成阶段和加载阶段:

  • 「RDB 文件生成阶段」从内存状态持久化成 RDB(文件)的时候,会对 key 进行过期检查,过期的键不会被保存到新的 RDB 文件中,因此 Redis 中的过期键不会对生成新RDB 文件产生任何影响;

  • 「RDB 加载阶段」如果 Redis 是主服务器运行模式的话,在载入 RDB 文件时,程序会对文件中保存的键进行过期检查,过期键不会被载入到数据库中。所以过期键不会对载入RDB 文件的主服务器造成影响;

  • 「RDB 加载阶段」如果 Redis 是从服务器运行模式的话,在载入 RDB 文件时,不论键是否过期都会被载入到数据库中。但由于主从服务器在进行数据同步时,从服务器的数据会被清空。所以一般来说,过期键对载入 RDB 文件的从服务器也不会造成影响。

AOF 文件分为两个阶段,AOF 文件写入阶段和重写阶段。

  • 「AOF 文件写入阶段」当 Redis 以 AOF 模式持久化时,如果数据库某个过期键还没被删除,那么 AOF 文件会保留此过期键,当此过期键被删除后,Redis 会向 AOF 文件追加一条 DEL 命令来显式地删除该键值;
  • 「AOF 重写阶段」执行 AOF 重写时,会对 Redis 中的键值对进行过期检查,已过期的键不会被保存到重写后的 AOF 文件中,因此不会对 AOF 重写造成任何影响。

1.8、主从模式过期键处理

当 Redis 运行在主从模式下时,从库不会进行过期扫描,从库对过期的处理是被动的。也就是即使从库中的 key 过期了,如果有客户端访问从库时,依然可以得到 key 对应的值,像未过期的键值对一样返回。

从库的过期键处理依靠主服务器控制,主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key。

2、内存淘汰机制

2.1、介绍

Redis的内存淘汰机制是为了解决内存占用过高的问题

在 Redis 的运行内存达到了某个阀值,就会触发内存淘汰机制,根据一定的策略来选择一些键值对进行删除,从而释放部分内存。

这个阀值就是我们设置的最大运行内存,此值在 Redis 的配置文件中可以找到,配置项为maxmemory

常见的内存淘汰策略有:

  1. LRU(Least Recently Used,最近最少使用):淘汰整个键值中最久未使用的键值;
  2. LFU(Least Frequently Used,最不经常使用):淘汰整个键值中最少使用的键
    值;
  3. Random(随机):随机选择键值对进行淘汰。
  4. noeviction(不进行数据淘汰):Redis3.0之后,默认的内存淘汰策略。它表示当运行内存超过最大设置内存时,不淘汰任何数据,而是不再提供服务,直接返回错误。

虽然虽然,但是后面就不介绍后两个策略了,主要介绍前两个策略:

  • 随机策略:想介绍也没东西介绍,就随缘抓几个起来噶掉这玩意
  • 不进行数据淘汰策略:想介绍也没东西介绍,就直接把门关了这玩意

2.2、LRU

LRU(Least Recently Used,最近最少使用)是Redis3.0之前默认的内存淘汰策略,它是淘汰整个键值中最久未使用的键值。

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

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

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

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

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

Redis 实现的 LRU 算法的优点:

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

但是 LRU 算法有一个问题,由于是随机采样的方式来淘汰数据,因此无法解决缓存污染问题。

比如应用一次读取了大量的数据,而这些数据只会被读取这一次,如果运气炸裂每次随机采样都采不到它们,那么这些数据会留存在 Redis 缓存中很长一段时间,造成缓存污染。

2.3、LFU

LFU 全称是 Least Frequently Used 翻译为最近最不常用的,是在Redis4.0新增的一种内存淘汰策略。

LFU 算法是根据数据访问次数来淘汰数据的,它的核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。

其实严格来说,LFU算法是根据数据访问频率来淘汰数据的。

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

LFU 算法相比于 LRU 算法的实现,多记录了「数据的访问频次」的信息。Redis 对象的结构如下:

typedef struct redisObject {
   ...
      
    // 24 bits,用于记录对象的访问信息
    unsigned lru:24;  
   ...
} robj;

Redis 对象头中的 lru 字段,在 LRU 算法下和 LFU 算法下使用方式并不相同。

在 LRU 算法中,Redis 对象头的 24 bits 的 lru 字段是用来记录 key 的访问时间戳,因此在 LRU 模式下,Redis可以根据对象头中的 lru 字段记录的值,来比较最后一次 key 的访问时间长,从而淘汰最久未被使用的 key。

在 LFU 算法中,Redis对象头的 24 bits 的 lru 字段被分成两段来存储, 高16bit存储 ldt (Last Decrement Time), 低8bit 存储 logc (Logistic Counter):

  • ldt 是用来记录 key 的访问时间戳;
  • logc 是用来记录 key 的访问频次,它的值越小表示使用频率越低,越容易淘汰,每个新加入的 key 的logc 初始值为 5。

注意, logc 并不是单纯的访问次数,而是访问频次(访问频率),因为 logc 会随时间推移而衰减的。

在每次 key 被访问时,会先对 logc 做一个衰减操作,衰减的值跟前后访问时间的差距有关系.如果上一次访问的时间与这一次访问的时间差距很大,那么衰减的值就越大,这样实现的 LFU 算法是根据访问频率来淘汰数据的,而不只是访问次数。

访问频率需要考虑 key 的访问是多长时间段内发生的。key 的先前访问距离当前时间越长,那么这个 key 的访问频率相应地也就会降低,这样被淘汰的概率也会更大。

对 logc 做完衰减操作后,就开始对 logc 进行增加操作,增加操作并不是单纯的自增,而是根据概率增加,如果 logc 越大的 key,它的 logc 就越难再增加。

所以,Redis 在访问 key 时,对于 logc 是这样变化的:

  1. 先按照上次访问距离当前的时长,来对 logc 进行衰减;
  2. 然后,再按照一定概率增加 logc 的值

redis.conf 提供了两个配置项,用于调整 LFU 算法从而控制 logc 的增长和衰减:

  • lfu-decay-time 用于调整 logc 的衰减速度,它是一个以分钟为单位的数值,默认值为1, lfu-decay-time 值越大,衰减越慢;
  • lfu-log-factor 用于调整 logc 的增长速度, lfu-log-factor 值越大,logc 增长越慢。

2.4、区别

3、区分过期删除和内存淘汰

内存淘汰机制

  • 当 Redis 的内存使用达到配置的最大内存限制时,内存淘汰机制会根据预先设置的策略来删除一些键值对,以释放内存空间。
  • 内存淘汰机制并不关心键是否设置了过期时间,它主要根据某种算法选择要淘汰的键值对,以腾出更多的内存空间,使得新的键值对可以被存储在内存中。
  • 常见的内存淘汰策略有 LRU(最近最少使用)、LFU(最不经常使用)、随机等。

过期删除机制

  • Redis 允许为键设置过期时间,过期删除机制是在键设置了过期时间后,当键过期时自动将其删除。
  • 过期删除机制并不是为了释放内存,而是为了使 Redis 中的数据始终保持最新的状态。
  • 过期的键值对将不再对外提供服务,直到下次有读或写操作访问该键时,Redis 会发现键已经过期,然后将其删除。

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

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

相关文章

3.openEuler物理存储及逻辑卷管理(一):磁盘存储挂载与使用

openEuler OECA认证辅导,标红的文字为学习重点和考点。 如果需要做实验,建议下载麒麟信安、银河麒麟、统信等具有图形化的操作系统,其安装与openeuler基本一致。 磁盘大类: HDD, (Hard Disk Drive的缩写) : 由一个或者多个铝制或者玻璃制成的磁性碟 片,磁头,…

流计算之Flink

文章目录 概要有界无界流集群JobManagerTaskManagersTasks 和算子链Task Slots 和资源 小结 概要 Apache Flink 是一个框架和分布式处理引擎,用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模…

深度解析:Integer.parseInt() 源码解读

深度解析:Integer.parseInt() 源码解读 关键要点 解析字符:用于将字符转换为对应的数字值 Character.digit(s.charAt(i),radix) 确定limit:根据正负号分别设定 int limit -Integer.MAX_VALUE;【正】 limit Integer.MIN_VALUE;【负】 负数…

通过几天JAVA学习总结和查询资料编写一个学生管理系统(简单版)

目录 需求分析 需求: 分析: 学生管理系统开始菜单界面 学生类: 功能实现: 添加功能 删除功能 修改功能 查询功能 项目实现 项目实现分析: 1.实现菜单选择功能 2.实现添加学生信息功能 3.实现删除学生信…

【DAY04 软考中级备考笔记】数据结构基本结构和算法

数据结构基本结构和算法 2月25日 – 天气:晴 周六玩了一天,周天学习。 1. 什么是数据结构 数据结构研究的内容是一下两点: 如何使用程序代码把现实世界的问题信息化如何用计算机高效地处理这些信息从创造价值 2. 什么是数据 数据是信息的…

Pod 异常问题排查

文章目录 前言1、诊断流程2、常用的排查方法3、Pod 状态、原因分析及故障处理总结 前言 通过 Kubernetes 部署服务,服务并未按照计划正常提供服务。如何通过 Pod 状态,进行异常问题的定制就显得特别重要了! 1、诊断流程 2、常用的排查方法 …

基于ZYNQ的PCIE高速数据采集卡的设计(一)

作为信息处理的第一步,数据采集的作用越来越重要。目前,数据采集已经在航 空、民用、军事、医疗等领域得到广泛应用。随着相关技术的不断发展,信号频率越 来高,带宽越来越大,使得数据采集技术逐渐向高速大数据的方向…

最新计算机毕业设计题目100例

文章目录 0 前言1 计算机毕设选题推荐2 开题指导3 最后 0 前言 大家好!大四的同学们毕业设计即将开始了,你们做好准备了吗? 学长给大家精心整理了最新的计算机毕业设计选题,希望能为你们提供帮助。如果在选题过程中有任何疑问&a…

C++之std::tuple(二) : 揭秘底层实现原理

相关系列文章 C之std::tuple(二) : 揭秘底层实现原理 C三剑客之std::any(一) : 使用 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二):深入剖析 深入理解可变参数(va_list、std::initializer_list和可变参数模版) st…

创作纪念日:记录我的成长与收获

机缘 一开始是在我深入学习前端知识的Vue.js框架遇到了一个问题,怎么都解决不了,心烦意乱地来csdn上找解决方法。开心的是真被我找到了,真的很感恩,也意识到在这个平台上分享自己的经验是多么有意义的事情,可能随便的…

外包干了3个月,技术倒退明显...

先说情况,大专毕业,18年通过校招进入湖南某软件公司,干了接近6年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…

在使用nginx的时候快速测试配置文件,并重新启动

小技巧 Nginx修改配置文件后需要重新启动,常规操作是启动在任务管理器中关闭程序然后再次双击nginx.exe启动,但是使用命令行就可以快速的完成操作。 将cmd路径切换到nginx的安装路径 修改完成配置文件后 使用 nginx -t校验nginx 的配置文件是否出错 …

相机选型介绍

摄影测量中,相机是非常重要的角色,合适的相机产出合适的图像,得到合适的重建精度,这是相机的重要性。 您也许第一反应是,摄影测量所需的理想相机,是有着超高分辨率的相机,但事实可能并非如此&a…

HTML+CSS+JS:轮播组件

效果演示 一个具有动画效果的卡片元素和一个注册表单,背景为渐变色,整体布局简洁美观。 Code <div class="card" style="--d:-1;"><div class="content"><div class="img"><img src="./img/果果k_01.jpg…

【全网首发】上周申请的谷歌Gemini 1.5 Pro已通过!百万token的Gemini 1.5 Pro开箱测试(一)

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【接口加密】接口加密的未来发展与应用场景

目录 3.1 接口加密与区块链技术的结合 3.1.1 区块链技术的安全特性与优势 3.1.2 接口加密在区块链中的应用案例 3.2 接口加密与物联网安全 3.2.1 物联网安全的挑战与需求 3.2.2 接口加密在物联网领域的实际应用 3.3 接口加密在金融与电子商务领域的应用 随着信息技术的不…

【初始RabbitMQ】延迟队列的实现

延迟队列概念 延迟队列中的元素是希望在指定时间到了之后或之前取出和处理消息&#xff0c;并且队列内部是有序的。简单来说&#xff0c;延时队列就是用来存放需要在指定时间被处理的元素的队列 延迟队列使用场景 延迟队列经常使用的场景有以下几点&#xff1a; 订单在十分…

Spring Boot 笔记 025 主界面

1.1 路由搭建 1.1.1 安装vue router npm install vue-router4 1.1.2 在src/router/index.js中创建路由器&#xff0c;并导出 import { createRouter, createWebHistory } from vue-router//导入组件 import LoginVue from /views/Login.vue import LayoutVue from /views/La…

设计模式学习笔记 - 面向对象 - 8.实践:贫血模型和充血模型的原理及实践

1.Web开发常用的贫血MVC架构违背OOP吗&#xff1f; 前面我们依据讲过了面向对象四大特性、接口和抽象类、面向对象和面向过程编程风格&#xff0c;基于接口而非实现编程和多用组合少用继承设计思想。接下来&#xff0c;通过实战来学习如何将这些理论应用到实际的开发中。 大部…

Redis实现滑动窗口限流

常见限流算法 固定窗口算法 在固定的时间窗口下进行计数&#xff0c;达到阈值就拒绝请求。固定窗口如果在窗口开始就打满阈值&#xff0c;窗口后半部分进入的请求都会拒绝。 滑动窗口算法 在固定窗口的基础上&#xff0c;窗口会随着时间向前推移&#xff0c;可以在时间内平滑控…