谈谈 Redis 的过期策略?
2026年01月01日
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
谈谈 Redis 的过期策略?
面试考察点
面试官提出这个问题,通常意在考察以下几个层面:
- 对缓存数据生命周期管理的理解:你是否清楚 Redis 作为缓存或数据库,需要有自动清理过期数据的能力。
- 对 Redis 核心机制的掌握深度:不仅仅是知道策略名称,更想知道你对其 底层实现原理、协同工作方式 的了解程度。
- 对系统资源与性能权衡的认知:如何设计过期策略才能在 内存利用率、CPU 消耗、数据一致性 之间取得平衡。
- 理论联系实际的能力:能否将策略与 内存淘汰策略 结合,并在实际开发中(如缓存穿透、雪崩)正确应用。
核心答案
Redis 的过期策略主要包含两个核心机制:惰性删除 和 定期删除。它们是 Redis 用来清理过期 Key、回收内存的主要手段。此外,当这些策略无法及时释放足够内存时,会触发 内存淘汰策略 作为最后防线。
简单概括:
- 惰性删除:访问 Key 时才检查并删除。
- 定期删除:周期性抽样检查并删除。
- 内存淘汰策略:内存不足时,按规则淘汰 Key(包括未过期的)。
深度解析
原理/机制
-
惰性删除 (Lazy Expiration)
- 原理:当客户端尝试访问一个 Key 时,Redis 会先检查该 Key 是否设置了过期时间以及是否已过期。如果过期,则立即删除该 Key,并向客户端返回
nil。这是一种 被动、延迟 的清理方式。 - 优点:对 CPU 友好,只有在必须时才进行删除操作,不会在无关的 Key 上消耗计算资源。
- 缺点:对内存不友好。如果一个过期 Key 永远不再被访问,它将永远占用内存,造成 内存泄漏。惰性删除是保证 Redis 性能的第一道屏障。
- 原理:当客户端尝试访问一个 Key 时,Redis 会先检查该 Key 是否设置了过期时间以及是否已过期。如果过期,则立即删除该 Key,并向客户端返回
-
定期删除 (Periodic Expiration)
- 原理:为了弥补惰性删除的缺陷,Redis 会 周期性 地(默认每秒 10 次,即每 100ms)主动执行一次过期 Key 清理。这个过程是自适应的:
- 每次从设置了过期时间的 Key 字典中,随机抽取一定数量(默认 20 个)的 Key。
- 检查并删除其中已过期的 Key。
- 如果本轮抽查中,过期 Key 的比例超过 25%,则 重复执行 抽查删除过程,直到过期 Key 比例降至 25% 以下,或者本次定期删除耗时过长(防止阻塞主线程)。
- 优点:通过限制执行时间和频率,减少了对主线程的阻塞,同时一定程度上减少了“永远不访问的过期 Key”造成的内存浪费。
- 缺点:它仍然是一种 抽样 和 妥协 的方案。难以完全精确地删除所有过期 Key,在 Key 数量巨大时,仍可能有部分过期 Key 残留。
- 原理:为了弥补惰性删除的缺陷,Redis 会 周期性 地(默认每秒 10 次,即每 100ms)主动执行一次过期 Key 清理。这个过程是自适应的:
-
内存淘汰策略 (Eviction Policies)
- 定位:这是过期策略的 补充和兜底 机制。当 Redis 内存使用达到
maxmemory配置的阈值,且新写入数据时,如果惰性和定期删除没能及时释放空间,就会触发内存淘汰。 - 8 种策略(Redis 7.x):
noeviction(默认):拒绝新写入,报错。适用于确需保留全部数据的场景。allkeys-lru/volatile-lru:使用 LRU(最近最少使用)算法淘汰 Key。前者针对所有 Key,后者只针对设置了过期时间的 Key。allkeys-lfu/volatile-lfu:使用 LFU(最不经常使用)算法淘汰 Key。LFU 比 LRU 更能精准识别热点数据。allkeys-random/volatile-random:随机淘汰 Key。volatile-ttl:优先淘汰 过期时间更短(TTL 更小) 的 Key。
注意:
volatile-xxx策略只在设置了过期时间的 Key 中淘汰,但如果这部分内存不足以满足新需求,依然会触发noeviction类似的行为(报错)。生产环境常用allkeys-lru或allkeys-lfu。 - 定位:这是过期策略的 补充和兜底 机制。当 Redis 内存使用达到
协同工作原理
这两种删除策略与内存淘汰策略共同构成了 Redis 的内存管理闭环:
// 伪代码逻辑示意
public Object get(String key) {
// 1. 惰性删除:访问时检查
if (key.expired) {
deleteKey(key);
return null;
}
return data;
}
public void periodicTask() {
// 2. 定期删除:后台循环任务
do {
sampledKeys = randomSample(expiredKeyDict, 20);
deletedCount = deleteExpiredKeys(sampledKeys);
} while (deletedCount / 20.0 > 0.25 && timeLimitNotExceeded());
}
public boolean writeData(String key, Object value) {
// 3. 写入前检查内存,触发淘汰策略
if (usedMemory >= maxMemory) {
if (!executeEvictionPolicy()) { // 执行配置的淘汰策略(如LRU)
if (policy == “noeviction”) {
throw new RedisOOMError(); // 内存不足错误
}
}
}
// ... 执行写入
}
- 每次 读取操作 都伴随一次惰性删除检查。
- 定期任务 每秒运行多次,抽样清理,防止内存无限增长。
- 当内存触顶,写入操作 会触发配置的淘汰策略,移除一些 Key(可能是过期的,也可能是未过期的)来腾出空间。
最佳实践与常见误区
-
最佳实践:
- 根据业务形态选择淘汰策略:例如,缓存系统优先使用
allkeys-lru;需要保留热点长期数据、只缓存临时数据的场景,可使用volatile-lru。 - 合理设置过期时间:避免大量 Key 在同一时间点过期,防止定期删除压力陡增和缓存雪崩。可以添加随机偏移值,例如:
expire = base_time + random(0, 300)。 - 监控
expired_keys指标:通过info stats命令查看,了解过期 Key 的清理速度是否正常。
- 根据业务形态选择淘汰策略:例如,缓存系统优先使用
-
常见误区:
- 误区一:“设置了过期时间,Key 到点就会立即被删除。” —— 错。删除依赖于惰性访问或定期扫描,存在延迟。
- 误区二:“
volatile-lru只淘汰过期的 Key。” —— 错。它只从 设置了过期时间的 Key 池子 里淘汰,但淘汰的依据是 LRU 算法,被淘汰的 Key 在淘汰那一刻可能并没过期。 - 误区三:“内存淘汰策略可以替代过期策略。” —— 错。过期策略是主动管理生命周期,淘汰策略是内存不足时的应急措施。二者目的不同,需配合使用。
总结
Redis 通过 惰性删除 和 定期删除 的 组合拳 来管理 Key 过期,在 CPU 和内存效率之间取得了平衡,并以 可配置的内存淘汰策略 作为内存不足时的最终保障;理解这套机制对于设计高性能、高可用的缓存架构至关重要。