Redis 过期删除与内存淘汰机制

Redis 过期删除与内存淘汰机制深度解析

在 Redis 的内存管理体系中,过期删除机制负责清理已设置过期时间的无效键 ,内存淘汰机制则在 Redis 内存达到阈值时主动释放内存空间,二者协同工作,确保 Redis 在有限内存下高效、稳定运行,是 Redis 作为缓存中间件的核心设计之一。本文将从实现原理、工作流程、核心参数、使用场景等维度,全面解析这两大机制,并梳理二者的关联与区别。

一、Redis 过期删除机制

Redis 并未采用单一的过期键清理方式,而是结合惰性删除定期删除 的组合策略,在 CPU 资源占用和内存回收效率之间做了最优平衡。在此之前,先了解 Redis 存储过期键的核心结构 ------过期字典(expires dict) :当通过EXPIRE key secondsSET key value EX seconds等命令为键设置过期时间时,Redis 会将键和其对应的绝对时间戳(如 2026-01-30 12:00:00)存入过期字典,该字典基于哈希表实现,可实现 O (1) 时间复杂度的过期时间查询,是过期删除机制的基础。

1. 惰性删除(Lazy Deletion):访问时的 "最后把关"

核心思想 :不主动扫描和删除过期键,仅当客户端读写访问某个键 时,才检查其是否过期,若过期则立即清理,属于被动清理策略

工作流程
  1. 客户端执行GETHSETDEL等任意读写命令操作某个键;
  2. Redis 先查询过期字典,判断该键是否存在过期时间且当前时间已超过该时间戳;
  3. 若过期:立即将键从数据库字典过期字典 中移除(Redis 4.0 + 可配置异步删除),返回nil或对应空结果;
  4. 若未过期 / 无过期时间:正常执行客户端命令,返回对应结果。
核心优缺点
  • CPU 友好:仅在必要时执行过期检查,避免无意义的 CPU 消耗,实现逻辑简单;
  • 内存占用风险:若大量过期键长期未被访问,会一直占用内存,造成 "内存泄漏",这也是其无法单独使用的核心原因。
底层实现

由 Redis 源码db.c中的expireIfNeeded函数实现,所有键的访问 / 修改操作前都会调用该函数,是过期键清理的 "最后一道防线"。

2. 定期删除(Periodic Deletion):主动的 "抽样巡逻"

核心思想 :Redis 通过后台定时任务周期性地随机抽样 过期键并清理,属于主动清理策略,专门解决惰性删除的内存积压问题。

工作流程

Redis 的服务器核心定时任务serverCron会执行定期删除逻辑(由activeExpireCycle函数实现),默认流程如下:

  1. 触发频率:由配置hz控制,默认每秒执行 10 次(每 100ms 一次);
  2. 随机抽样:每次从过期字典中随机抽取20 个键进行过期检查;
  3. 批量删除:删除抽样中已过期的键,若过期键占比超过 25% ,则重复抽样删除流程;
  4. 时长限制:为避免阻塞主线程,单次定期删除的最大执行时间不超过 25ms,超时则立即终止,等待下一次执行。

简单来说,过期键占比越高,Redis 的主动清理力度越大,但始终控制在不影响正常请求的范围内。

核心优缺点
  • 主动释放内存:能清理长期未被访问的过期键,避免内存无限积压;
  • 可控性强:可通过配置调整触发频率,适配不同业务的负载;
  • 清理不彻底:由于随机抽样和时长限制,无法保证所有过期键都被及时清理,仍会有少量过期键残留。

3. 组合策略的协同逻辑

惰性删除和定期删除并非独立工作,而是互补配合

  • 定期删除:负责主动巡逻,清理大部分 "僵尸过期键"(长期未访问),控制内存整体占用;
  • 惰性删除:负责最后把关,确保任何被访问的键都是 "干净的",避免客户端获取到过期数据。

可以用一个通俗的比喻理解:定期删除像 "保洁阿姨定时抽查打扫",惰性删除像 "主人用东西前发现脏了立刻清理",二者结合既保证了环境整洁,又不会浪费过多精力。

4. 过期删除的核心配置

可通过redis.confCONFIG SET命令调整,适配业务场景:

配置项 默认值 作用
hz 10 定时任务执行频率,值越大定期删除越频繁,CPU 占用略高
lazyfree-lazy-expire no Redis 4.0+,是否异步删除过期键,开启可避免大键删除阻塞主线程
maxmemory-samples 5 抽样估算过期键分布的样本数,不影响定期删除的 20 个键抽样规则

5. 特殊场景:持久化与主从的过期键处理

过期删除的逻辑在持久化和主从架构中存在特殊处理,避免数据不一致:

  1. RDB 持久化:生成 RDB 文件时,会过滤过期键,不写入文件;主库加载 RDB 时过滤过期键,从库加载时则全部载入(由主库同步删除指令);
  2. AOF 持久化 :过期键被删除时,会向 AOF 文件写入DEL命令,保证 AOF 重写后无过期键;
  3. 主从架构 :过期键的删除操作仅在主库 执行,从库不主动处理过期键,而是通过主库的DEL同步指令实现过期清理,保证主从数据一致。

二、Redis 内存淘汰机制

当 Redis 的已使用内存达到配置的maxmemory阈值 时,过期删除机制已无法满足内存释放需求(比如大部分键未设置过期时间),此时会触发内存淘汰机制------ 主动淘汰部分键值对,释放内存以保证后续写操作正常执行,这是 Redis 内存管理的 "最后一道屏障"。

1. 核心触发条件

  1. Redis 已使用内存 ≥ maxmemory(可通过CONFIG SET maxmemory 4GB设置,单位支持 b/k/m/g);
  2. 后续有新的写操作(SETLPUSH等),无空闲内存可用;
  3. 若未配置maxmemory(默认无限制),则永远不会触发内存淘汰(Linux 环境下可能因物理内存不足被 OOM 杀死)。

2. 8 种核心淘汰策略

Redis 提供 8 种淘汰策略,分为针对过期键针对所有键 两大类,可通过maxmemory-policy配置,Redis 4.0 + 新增 LFU 相关策略,覆盖更多业务场景。

基础策略(Redis 2.8+)
策略名 淘汰范围 淘汰规则 适用场景
noeviction 不淘汰任何键,写操作返回 OOM 错误 核心数据存储,不允许丢失数据
volatile-lru 仅设置过期时间的键 淘汰最近最少使用的键 混合存储(核心键无过期,缓存键有过期)
allkeys-lru 所有键 淘汰最近最少使用的键 纯缓存场景,优先保留热点数据
volatile-random 仅设置过期时间的键 随机淘汰键 过期键数量大,无明显热点
allkeys-random 所有键 随机淘汰键 数据访问均匀,无热点区分
volatile-ttl 仅设置过期时间的键 淘汰TTL 最短(即将过期)的键 需精准控制过期键的淘汰顺序
新增策略(Redis 4.0+)
策略名 淘汰范围 淘汰规则 适用场景
volatile-lfu 仅设置过期时间的键 淘汰最不经常使用的键 热点数据随时间变化,LRU 效果不佳
allkeys-lfu 所有键 淘汰最不经常使用的键 纯缓存,访问频率是核心热点指标

3. 核心算法:LRU 与 LFU 的实现

Redis 并未实现严格的 LRU/LFU 算法 (会占用大量内存存储访问记录),而是采用近似 LRU/LFU ,通过随机抽样 实现,兼顾性能和效果,抽样数由maxmemory-samples(默认 5)控制,值越大越接近严格算法,CPU 占用略高。

  • LRU(Least Recently Used) :最近最少使用,核心是 "淘汰最后一次访问时间最久的键";
  • LFU(Least Frequently Used) :最不经常使用,核心是 "淘汰访问频率最低的键",Redis 通过 8bit 存储访问频次的衰减值,解决 LRU 在 "突发访问后长期闲置" 场景的淘汰偏差问题。

4. 内存淘汰的执行流程

当触发内存淘汰时,Redis 的执行逻辑如下:

  1. 根据配置的淘汰策略,从指定范围(过期键 / 所有键)中随机抽样maxmemory-samples个键;
  2. 按照 LRU/LFU/TTL 等规则,从抽样结果中选择 "最该淘汰" 的键;
  3. 删除该键并释放内存,若内存仍超过maxmemory,重复步骤 1-3;
  4. 若淘汰后内存低于阈值,或无符合条件的键可淘汰(如volatile-*策略下无过期键),则停止淘汰,若为noeviction策略则返回写错误。

三、过期删除与内存淘汰的核心区别

很多开发者会混淆这两大机制,核心区别在于触发条件、清理对象、设计目标的不同,具体对比如下:

对比维度 过期删除机制 内存淘汰机制
触发条件 键过期 +(访问 / 定期抽样),与内存使用量无关 Redis 已用内存 ≥ maxmemory,仅写操作触发
清理对象 设置了过期时间且已过期的键 按策略划分:可是过期键,也可是所有键
设计目标 清理过期的无效数据,保证数据有效性 释放内存,避免 OOM,保证 Redis 写可用性
执行时机 访问时实时执行 / 后台定时执行 内存达到阈值时,写操作时触发
是否可关闭 不可关闭(惰性删除是基础逻辑) 可通过maxmemory-policy noeviction变相关闭

核心关联:内存淘汰机制可视为过期删除机制的 "补充和升级"------ 当过期删除无法满足内存释放需求时,由内存淘汰机制接手,二者共同构成 Redis 完整的内存回收体系。

四、生产环境的最佳实践

1. 配置建议

根据业务场景选择合适的配置,核心推荐如下:

纯缓存场景(无核心数据,允许丢失)
  • maxmemory:根据服务器内存设置,建议预留 20% 以上物理内存(如 16G 服务器设置 12G);
  • maxmemory-policy:推荐allkeys-lfu(访问频率更贴合热点),次之allkeys-lru
  • hz:调整为 20-50(提高定期删除频率,减少过期键积压);
  • 开启lazyfree-lazy-expire yes(异步删除过期键,避免阻塞)。
混合存储场景(核心数据 + 缓存数据)
  • 核心数据:不设置过期时间,保证数据不被淘汰;
  • 缓存数据:统一设置过期时间;
  • maxmemory-policy:推荐volatile-lfu/volatile-lru(仅淘汰缓存键,保护核心数据);
  • maxmemory:合理评估核心数据内存占用,预留足够空间。
核心数据存储场景(不允许数据丢失)
  • maxmemory:不设置或设置足够大的值;
  • maxmemory-policynoeviction(拒绝写操作,避免数据被淘汰);
  • 依赖过期删除机制清理过期数据,配合hz=20提高清理效率。

2. 性能优化要点

  1. 避免大量键同时过期 :若批量设置过期时间(如整点过期),会导致定期删除集中执行,造成 CPU 峰值,建议增加随机过期时间 (如EX 3600 + random(0, 600));
  2. 大键单独处理:对 Hash、List 等大键,避免直接设置过期时间,可拆分为小键,防止删除时阻塞主线程;
  3. 监控核心指标 :通过INFO memory查看expired_keys(已过期删除键数)、evicted_keys(已淘汰键数),通过INFO stats查看内存使用趋势,及时调整配置;
  4. 开启主动碎片整理 :设置activedefrag yes,清理内存碎片,提高内存利用率。

3. 常见问题排查

  1. Redis 内存占用过高但无大键 :大概率是过期键积压,检查hz是否过小,可提高至 20-50,同时检查expired_keys指标;
  2. 出现大量 evicted_keys :说明maxmemory设置过小,或淘汰策略不合适,需增大内存或调整策略;
  3. 主线程阻塞 :检查是否有大键过期 / 被淘汰,开启异步删除(lazyfree相关配置),拆分大键。

五、总结

Redis 的过期删除和内存淘汰机制是其高性能的核心设计之一,充分体现了 Redis 在性能与资源之间的平衡思想:

  1. 过期删除通过惰性 + 定期的组合策略,在保证 CPU 效率的同时,尽可能清理过期数据,保证数据有效性;
  2. 内存淘汰机制在内存达到阈值时,通过近似 LRU/LFU 等策略释放内存,保证 Redis 的写可用性;
  3. 二者分工明确、协同工作,过期删除负责 "数据有效性",内存淘汰负责 "内存可用性"。

在生产环境中,无需过度纠结底层实现,核心是根据业务场景选择合适的配置,并通过监控指标持续优化,让 Redis 的内存管理能力适配业务的实际需求。

相关推荐
其古寺2 小时前
Spring事务嵌套异常处理深度解析
经验分享
笨蛋不要掉眼泪2 小时前
Spring Boot集成LangChain4j:与大模型对话的极速入门
java·人工智能·后端·spring·langchain
-孤存-2 小时前
MyBatis数据库配置与SQL操作全解析
数据库·mybatis
wdfk_prog3 小时前
[Linux]学习笔记系列 -- [drivers][i2c]i2c-dev
linux·笔记·学习
土拨鼠烧电路3 小时前
笔记03:业务语言速成:“人、货、场”模型与IT系统全景图
笔记
2301_822366353 小时前
使用Scikit-learn构建你的第一个机器学习模型
jvm·数据库·python
2301_812731414 小时前
CSS3笔记
前端·笔记·css3
万邦科技Lafite5 小时前
一键获取京东商品评论信息,item_reviewAPI接口指南
java·服务器·数据库·开放api·淘宝开放平台·京东开放平台
小乔的编程内容分享站5 小时前
记录使用VSCode调试含scanf()的C语言程序出现的两个问题
c语言·开发语言·笔记·vscode