Redis内存回收-过期key处理

一、引言:缓存的"保质期"管理

在 Redis 中,我们经常为数据设置一个"保质期"(TTL, Time To Live),例如:

bash 复制代码
SET session:12345 "user_data" EX 1800  # 30分钟后过期

这能有效防止内存被无用数据无限占用。但随之而来的问题是:当一个 Key 到了它的"保质期",Redis 是如何知道并清理它的呢?

如果立即删除,会消耗大量 CPU;如果永不删除,又会造成内存泄漏。这是一个经典的CPU 资源与内存资源之间的权衡问题。

Redis 给出的答案是:不采用单一策略,而是将两种互补的策略------惰性删除与定期删除------结合起来,形成一套高效且平衡的过期 Key 处理机制

💡 核心价值
理解这套机制,不仅能让你设计出更高效的缓存方案,更能回答面试中关于 Redis 内存管理的经典问题


二、为什么不用"定时删除"?

在讨论 Redis 的方案之前,我们先看看一个看似最直接的方案为何被弃用。

2.1 定时删除(Timed Deletion)方案

  • 原理:为每个设置了过期时间的 Key 创建一个定时器(Timer)。一旦时间到,定时器立刻触发,将该 Key 删除。
  • 优点 :对内存最友好,过期 Key 能被立即释放
  • 致命缺点CPU 开销巨大!想象一下,如果有百万甚至千万个 Key 同时设置了过期时间,那么就需要维护同等数量的定时器。这不仅会消耗大量内存来存储定时器本身,其频繁的上下文切换和回调执行会严重拖慢 Redis 主线程的响应速度,违背了 Redis 追求极致性能的初衷。

因此,Redis 没有采用这种"理想化"的定时删除策略。


三、Redis 的双剑合璧:惰性删除 + 定期删除

为了在 CPU 和内存之间取得最佳平衡,Redis 巧妙地结合了两种策略。

3.1 策略一:惰性删除(Lazy Expiration)

原理

这是一种"被动式 "或"按需式 "的删除策略。Redis 并不会主动去关心一个 Key 是否过期,而是在每次客户端尝试访问这个 Key 时 ,才去检查它是否已经过期。如果过期,则立即删除,并向客户端返回空值(如 nil)。

你可以把它想象成超市里顾客结账时,收银员才检查商品是否过期。

优点
  • CPU 友好:只有在真正需要访问数据时才进行检查,避免了不必要的计算开销。
  • 实现简单:逻辑清晰,易于集成到命令执行流程中。
缺点
  • 内存不友好 :如果一个过期的 Key 永远不再被访问 ,那么它将一直占用着宝贵的内存,直到 Redis 重启或触发其他内存回收机制。这在某些场景下可能导致内存泄漏。

3.2 策略二:定期删除(Periodic Expiration)

原理

这是一种"主动式 "的删除策略,用于弥补惰性删除的不足。Redis 的主线程会周期性地(默认每秒 10 次,即每 100 毫秒一次)随机抽查一部分设置了过期时间的 Key,并将其中已过期的 Key 删除。

具体流程如下(以默认的 SLOW 模式为例):

  1. 随机采样 :从 Redis 的"过期字典"(一个专门存放所有带 TTL 的 Key 的哈希表)中,随机选取 20 个 Key
  2. 检查与删除:遍历这 20 个 Key,删除所有已过期的 Key。
  3. 动态调整 :如果在这 20 个 Key 中,过期 Key 的比例超过 25% (即 5 个以上),则说明过期 Key 的密度很高,于是 Redis 会重复步骤 1,继续采样和删除,直到某次采样中过期比例低于 25%,或者单次操作耗时接近 25 毫秒(为了避免长时间阻塞主线程)。

📌 注意 :Redis 6.0+ 引入了 FAST 模式,它会在事件循环的 beforeSleep 阶段执行,每次只花费 1 毫秒,频率更高但每次处理量更小。

优点
  • 内存友好:即使某些 Key 永远不被访问,也能通过后台的定期扫描被逐渐清理掉,有效防止了内存泄漏。
  • 可控的 CPU 开销:通过限制每次操作的时间和采样数量,将 CPU 消耗控制在一个可接受的范围内。
缺点
  • 无法保证实时性:过期 Key 不会被立即删除,存在一定的延迟。
  • 仍有遗漏可能:由于是随机采样,不能保证在短时间内清理掉所有过期 Key。

四、协同工作:一张图看懂整体流程

复制代码
+---------------------+
|    Client Request   |
| (e.g., GET mykey)   |
+----------+----------+
           |
           v
+---------------------+
|   Main Thread       |
|                     |
| 1. Check if 'mykey' |<---+
|    has expired?     |    |
|                     |    |
| YES --> Delete it   |    | 3. Periodic Task
|         & return nil|    | (every 100ms)
|                     |    |
| NO  --> Return value|    |
+----------+----------+    |
           |               |
           |               v
           |      +------------------+
           +----->| Randomly sample  |
                  | 20 keys from     |
                  | the expire dict  |
                  |                  |
                  | Delete expired   |
                  | ones             |
                  +------------------+
  • 路径1(蓝色) :代表惰性删除。由客户端请求触发。
  • 路径2(绿色) :代表定期删除。由 Redis 内部的定时任务触发。

这两种策略相互配合,共同构成了 Redis 健壮的过期 Key 回收体系。


五、配置与调优

虽然 Redis 的默认策略已经非常优秀,但在极端场景下,我们也可以对其进行微调。

  • hz 配置项 :控制 Redis 执行后台任务(包括定期删除)的频率。默认值为 10,表示每秒 10 次。将其调高(如 100)可以让定期删除更积极,更快地回收内存,但会增加 CPU 负担。反之亦然。
  • active-expire-effort (Redis 7.0+):直接控制定期删除的 CPU 努力程度,取值 1-10,默认为 1。值越大,每次定期删除任务会尝试做更多工作。

六、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
KaMeidebaby7 小时前
卡梅德生物技术快报|PROTAC 药物降解蛋白原理及数据库平台开发全流程
前端·数据库·其他·百度·新浪微博
鱼鳞_7 小时前
苍穹外卖-Day05(Redis)
java·redis
是码龙不是码农7 小时前
数据库主键选型:为什么别用自增 ID?
java·数据库
IpdataCloud7 小时前
企业IT管理中,如何通过IP地址查询定位快速溯源异常终端?用IP离线库实现
服务器·网络·数据库·tcp/ip
罗超驿7 小时前
20.MySQL事务隔离级别示例详解(脏读、不可重复读、幻读)
java·数据库·mysql·面试
钝挫力PROGRAMER8 小时前
实战经验:如何修复 MariaDB 因 InnoDB 损坏导致的启动失败 (status=6/ABRT)
数据库·mariadb
我是一颗柠檬8 小时前
【MySQL全面教学】MySQL基础与环境搭建Day1(2026年)
数据库·后端·sql·mysql·database
我是一颗柠檬8 小时前
【MySQL全面教学】MySQL数据类型详解Day2(2026年)
数据库·后端·sql·mysql·database
一只fish8 小时前
Oracle官方文档翻译《Database Concepts 26ai》第10章-SQL
数据库·oracle