全面整理Redis过期删除与内存淘汰策略底层原理机制

作为高性能的键值对数据库,Redis被广泛应用于缓存、会话存储等场景。在使用Redis的过程中,我们经常会遇到key过期、内存占用过高的问题,而这背后就涉及到Redis的核心机制------过期删除策略与内存淘汰策略。今天,我们就从原理出发,全面整理这些关键技术,争取一文搞懂Redis是如何平衡CPU占用与内存利用率的。

01

什么是过期删除策略?

Redis支持为key设置过期时间,为了保证过期键值对能被及时清理,避免无效数据占用内存,就需要一套专门的机制来处理过期key,这套机制就是过期删除策略

1. 如何判定key已过期?

当我们为key设置过期时间时,Redis会将该key与对应的过期时间存入一个**过期字典(expires dict)**中。这个字典的key是指向键对象的指针,value是一个long long类型的整数,代表key的过期时间点。

判定逻辑很简单,时间复杂度仅为O(1):

  • 若key不在过期字典中,或过期字典中的过期时间大于当前时间,说明key正常;

  • 否则,说明key已过期。

2. 三种核心过期删除策略详解

过期删除策略的核心矛盾的是"CPU占用"与"内存利用率"的平衡,不同策略给出了不同的解决方案,具体分为以下三种:

(1) 定时删除:时效性拉满,但CPU不友好

在设置key过期时间的同时,创建一个定时事件。当时间达到过期点时,由事件处理器自动执行key的删除操作。

定时删除优点是过期key能被立即删除,时效性最强,不会造成内存浪费;但缺点是如果存在大量过期key,定时事件会频繁触发删除操作,占用大量CPU资源,影响Redis的核心读写性能。

(2) 惰性删除:CPU友好,但可能浪费内存

不主动删除过期key,只有当客户端访问某个key时,才会检查该key是否过期。若过期则删除,否则返回正常键值对。

惰性删除优点是只在访问时才进行过期检查,几乎不占用额外CPU资源,对CPU最友好;但缺点是如果一个过期key长期不被访问,就会一直占用内存,造成内存空间的无效浪费(即"内存泄漏"风险)。

(3) 定期删除:折中方案,需精细配置

每隔一段时间,随机从数据库中抽取一定数量的key进行过期检查,并删除其中的过期key。

定期删除的优点是可以通过限制删除操作的执行时长和频率,平衡了CPU占用与内存浪费问题------既不会像定时删除那样频繁占用CPU,也不会像惰性删除那样任由过期key堆积;但缺点是时效性不如定时删除,性能不如惰性删除;且删除操作的时间间隔和抽取key的数量难以精准配置,配置不当可能导致CPU占用过高或内存清理不及时。

02

Redis的选择:惰性删除+定期删除

Redis没有选择单一的过期删除策略,而是采用"惰性删除+定期删除"的组合策略,在合理使用CPU时间和避免内存浪费之间取得最佳平衡。

1. Redis的惰性删除实现

当客户端访问或修改key时,Redis会先调用expireIfNeeded函数检查该key是否过期:

  • 若过期:删除该key(4.0版本后可通过lazyfree_lazy_expire参数配置同步删除或异步删除),返回null给客户端;

  • 若未过期:不做任何处理,返回正常键值对给客户端。

2. Redis的定期删除实现

定期删除的核心是"随机抽样+循环检查+时间上限",具体流程和配置如下:

  • 时间间隔:默认每秒执行10次,可通过redis.conf中的hz参数配置;

  • 抽样数量:代码中写死的常量,固定为20个key;

  • 执行流程:① 从过期字典中随机抽取20个key;② 检查并删除其中的过期key;③ 若本轮过期key占比超过25%(即超过5个),则重复步骤①-②,直到占比低于25%;

  • 安全机制:为避免循环过度导致线程卡死,设置了时间上限,默认单次定期删除循环不超过25ms。

03

特殊场景:主从模式下的过期键处理

当Redis运行在主从模式时,从库不会主动进行过期扫描,对过期key的处理完全被动:

  • 即使从库中的key已过期,若有客户端访问,仍会返回key的原值,如同未过期;

  • 过期key的删除由主库控制:主库检测到key过期时,会在AOF文件中添加一条del指令,同步到所有从库,从库通过执行该指令删除过期key。

04

持久化场景:过期键在RDB/AOF中的状态

Redis的持久化文件有RDB(Redis Database)和AOF(Append Only File)两种格式,过期键在这两种文件中的呈现和处理逻辑不同,具体分为生成/写入阶段和加载/重写阶段。

1. RDB文件中的过期键

  • 生成阶段:从内存持久化生成新RDB文件时,Redis会对key进行过期检查,过期key不会被写入新RDB文件,因此过期键不会影响RDB文件的生成。
  • 加载阶段:加载RDB文件时,处理逻辑取决于Redis的运行模式:如果是主服务器,则加载时会检查文件中的key,过期key不会被载入数据库;如果是从服务器,无论key是否过期,都会被载入数据库。但由于主从同步时从库数据会被清空,因此过期键对从库加载RDB文件也几乎没有影响。

2. AOF文件中的过期键

  • 写入阶段:以AOF模式持久化时,若过期key未被删除,AOF文件会保留该key的相关记录;当该key被删除后,Redis会向AOF文件追加一条del指令,显式标记该key的删除。
  • 重写阶段:执行AOF重写时,Redis会对内存中的键值对进行检查,过期key不会被写入重写后的AOF文件,因此过期键不会影响AOF重写。

05

内存淘汰策略:当Redis内存超限时怎么办?

当Redis的运行内存超过设置的最大内存时,会触发内存淘汰策略,删除符合条件的key,保障Redis高效运行。在此之前,我们需要先了解如何设置Redis的最大运行内存。

1. 如何设置Redis最大运行内存?

可通过redis.conf中的maxmemory <bytes>参数设置最大运行内存。不同位数的操作系统,默认值不同:

  • 64位操作系统:默认值为0,表示无内存限制。此时Redis不会检查可用内存,直到因内存不足崩溃;

  • 32位操作系统:默认值为3GB。因为32位系统最大仅支持4GB内存,预留部分内存给系统运行,避免Redis因内存不足崩溃。

2. 八种内存淘汰策略分类详解

Redis共有八种内存淘汰策略,可分为"不进行数据淘汰"和"进行数据淘汰"两大类,其中"进行数据淘汰"又可细分为"仅淘汰设置过期时间的key"和"淘汰所有key"两个小类。

(1) 不进行数据淘汰:
  • noeviction(Redis3.0后默认策略):运行内存超限时,不淘汰任何数据。此时新数据写入会报错,但查询、删除等操作可正常执行。
(2) 进行数据淘汰:

① 仅在设置了过期时间的key中淘汰:

  • volatile-random:随机淘汰设置了过期时间的任意key;
  • volatile-ttl:优先淘汰过期时间更早的key;

  • volatile-lru(Redis3.0前默认策略):淘汰设置了过期时间的key中最久未使用的key;

  • volatile-lfu(Redis4.0新增):淘汰设置了过期时间的key中最少使用的key。

② 在所有key中淘汰:

  • allkeys-random:随机淘汰任意key;

  • allkeys-lru:淘汰所有key中最久未使用的key;

  • allkeys-lfu(Redis4.0新增):淘汰所有key中最少使用的key。

3. 查看与修改内存淘汰策略

实际使用中,可通过以下方式查看和修改内存淘汰策略:

  • 查看策略:config get maxmemory-policy

  • 临时修改:config set maxmemory-policy <策略名>(重启Redis后失效);

  • 永久修改:在redis.conf中配置maxmemory-policy <策略名>(重启后仍有效)。

06

核心算法:LRU与LFU的实现与差异

在内存淘汰策略中,LRU和LFU是两种关键的淘汰算法,Redis对它们进行了优化实现,以适配自身的性能需求。

1. LRU算法:最近最少使用

LRU(Least Recently Used)即最近最少使用,核心思想是"淘汰最近最少被访问的数据"。传统LRU基于链表实现:

  • 链表元素按访问顺序排列,最新访问的key移到表头;

  • 淘汰时删除表尾元素(最久未使用)。

传统LRU的问题存在以下问题:

  • 额外空间开销:需要维护链表管理所有缓存数据;

  • 性能损耗:大量数据访问时,频繁移动链表元素会耗时,降低Redis性能。

为解决传统LRU的问题,Redis实现了近似LRU算法:

  • 在Redis对象结构体中添加"最后访问时间"字段;

  • 淘汰时采用随机采样:默认随机取5个key(可配置),淘汰其中最久未使用的key。

Redis近似LRU的实现,无需维护大链表,节省空间,也无需频繁移动元素,提升性能。但是缺点是无法解决"缓存污染"问题------若应用一次性读取大量仅使用一次的数据,这些数据会长期占用缓存,浪费内存。

2. LFU算法:最近最不常用

LFU(Least Frequently Used)即最近最不常用,核心思想是"淘汰访问频率最低的数据"。它通过记录数据的访问次数来判断优先级:访问次数越多,未来被访问的概率越高,越不容易被淘汰。LFU解决了LRU的缓存污染问题------一次性访问的大量数据,由于访问频率低,会被快速淘汰。

Redis利用对象头中24bits的lru字段实现LFU,该字段被分为两段:

  • 高16bit(ldt):记录key的最后访问时间戳;

  • 低8bit(logc):记录key的访问频次(非绝对访问次数,会随时间衰减)。

新key的logc初始值为5,每次访问key时,根据上次访问与当前的时间差,对logc进行衰减(时间差越大,衰减越多),然后再按概率增加logc的值(logc越大,增长越难)。此外,Redis提供两个配置项调整LFU的衰减和增长速度(在redis.conf中设置):

  • lfu-decay-time:单位为分钟,默认值1,控制logc的衰减速度(值越大,衰减越慢);

  • lfu-log-factor:控制logc的增长速度(值越大,logc增长越慢)。

07

总结

Redis的过期删除与内存淘汰策略,本质上是对"CPU资源"和"内存资源"的平衡艺术:

  • 通过"惰性删除+定期删除"组合,既避免了定时删除的CPU过载,也减少了惰性删除的内存浪费;

  • 主从模式和持久化场景下的过期键处理,保障了数据一致性;

  • 八种内存淘汰策略适配不同业务场景,而LRU/LFU算法的优化实现,进一步提升了缓存的合理性和性能。

掌握这些机制,能帮助我们在实际开发中更合理地配置Redis,避免过期key堆积、内存溢出等问题,让Redis发挥最佳性能。

你在使用Redis时,遇到过哪些关于过期key或内存的问题?欢迎在评论区留言讨论~

相关推荐
while(1){yan}20 小时前
Spring事务
java·数据库·spring boot·后端·java-ee·mybatis
盛世宏博北京21 小时前
高效环境管控:楼宇机房以太网温湿度精准监测系统方案
开发语言·数据库·php·以太网温湿度变送器
运维行者_21 小时前
2026 技术升级,OpManager 新增 AI 网络拓扑与带宽预测功能
运维·网络·数据库·人工智能·安全·web安全·自动化
gfdhy21 小时前
【C++实战】多态版商品库存管理系统:从设计到实现,吃透面向对象核心
开发语言·数据库·c++·microsoft·毕业设计·毕设
Elastic 中国社区官方博客21 小时前
Elasticsearch:上下文工程 vs. 提示词工程
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
小唐同学爱学习21 小时前
如何解决海量数据存储
java·数据库·spring boot·mysql
wWYy.1 天前
详解redis(15):缓存雪崩
数据库·redis·缓存
zzcufo1 天前
多邻国第五阶段第13部分
java·开发语言·数据库
这周也會开心1 天前
Redis相关知识点
数据库·redis·缓存
小白爱运维1 天前
MySQL升级8.0.44后登录报错-系统表不支持'MyISAM'存储引擎
数据库·mysql