Redis淘汰策略
Redis 整理(过期删除+内存淘汰+LRU/LFU/TTL)
一、Redis 过期 Key 的三种删除策略
什么时候清理过期键?Redis 针对设置了 expire 过期时间的 key,有三种删除策略,生产环境默认组合使用「惰性删除 + 定期删除」。
1. 定时删除
主动删除,Redis 未采用
- 原理:给 key 设置过期时间时,创建定时器,时间一到立刻删除 key。
- 优点:内存最友好,过期键能马上释放内存。
- 缺点:极度消耗 CPU;海量 key 同时过期时,定时器任务扎堆执行,阻塞 Redis 主线程,降低吞吐量。
2. 惰性删除
被动删除,Redis 默认开启
- 原理:过期 key 不会主动清理,只有客户端访问该 key时,Redis 先校验过期时间,已过期则直接删除并返回 nil。
- 优点:CPU 零额外开销,只在读写命中时做判断,性能无损耗。
- 缺点:内存不友好;若过期 key 长期无人访问,会永久占用内存,形成内存泄漏。
3. 定期删除
主动兜底,Redis 默认开启
- 原理:Redis 后台定时任务
serverCron(默认 100ms 执行一次),随机抽取一批带过期时间的 key 批量校验、删除。- 执行逻辑:每次随机抽 20 个过期 key,删除全部过期 key;若本轮过期 key 占比超 25%,重复扫描;单次扫描限时 25ms,防止阻塞主线程。
- 优点:折中方案,平衡 CPU 与内存,兜底清理长期无人访问的过期键。
- 缺点:随机抽样存在漏删可能,无法一次性清理全部过期 key。
4. 提炼后:Redis 实际过期清理机制
Redis 采用 惰性删除 + 定期删除 组合:
- 惰性删除:每次读写 key 时实时清理已过期 key;
- 定期删除:后台定时抽样扫描,兜底清理冷过期 key;
- 内存淘汰策略:内存打满写入新数据时,会额外清理过期 / 冷数据,三重保障释放内存。
二、Redis 8 种内存淘汰策略
内存达到 maxmemory 阈值时触发
触发时机
Redis 配置 maxmemory 限制最大内存,当写入新 key 导致内存超出阈值时,执行内存淘汰策略释放空间。
分类两大维度
- allkeys 系列:淘汰全部 key(包含永久无过期 key)
- volatile 系列:仅淘汰设置过过期时间的 key,永久业务数据保留
完整 8 种策略详解
-
noeviction(默认策略)不淘汰任何 key,写入新数据直接返回 OOM 报错;读操作正常执行,适合存放不可丢失的核心业务数据。
-
allkeys-lru(生产最常用)全局所有 key,淘汰
最近最少使用的 key;通用缓存场景首选。 -
volatile-lru
仅在带过期时间的 key 中,淘汰最久未访问的 key;适合区分永久数据 + 临时缓存的混合存储。 -
allkeys-lfu(Redis4.0 + 推荐)全局所有 key,淘汰
访问频次最低的 key;解决 LRU 历史热点缓存污染问题。 -
volatile-lfu
仅过期 key 范围内,淘汰访问次数最少的 key。 -
allkeys-random全局随机删除任意 key,性能最好,但缓存命中率极低,极少使用。
-
volatile-random仅在过期 key 中随机删除,无冷热区分。
-
volatile-ttl仅过期 key 中,优先淘汰
剩余存活时间最短、马上过期的 key;适合短期临时缓存场景。
生产选型总结
- 纯缓存、无永久业务数据:
allkeys-lfu/allkeys-lru - 混合存储(永久核心数据 + 临时缓存):
volatile-lru/volatile-lfu - 临时短期过期缓存:
volatile-ttl - 核心数据不可丢失:
noeviction(需手动扩容)
三、LRU / LFU / TTL 核心概念与底层实现
基础定义
-
LRU(Least Recently Used)最近最少使用
核心思想:时间维度,淘汰最久没有被访问的数据;假设近期访问的数据未来大概率再次使用。
-
LFU(Least Frequently Used)最不经常使用
核心思想:频次维度,淘汰一段时间内访问次数最少的数据;区分真正热点与偶尔访问的临时数据。
-
TTL(Time To Live)剩余过期时间
仅针对带过期时间的 key,优先清理马上到期的数据,不关心访问行为。
1. 标准 LRU 理想实现(面试手写 LRU 缓存考点)
要求:get/put 操作时间复杂度 O(1)
数据结构组合:HashMap + 双向链表

LRU哈希+双向链表结构
- HashMap:存储 key → 链表节点映射,实现 O (1) 快速查找节点;
- 双向链表:维护访问时序
- 链表头部:最新访问 / 新增节点(热数据)
- 链表尾部:最久未访问节点(淘汰目标)
执行流程
- get 命中 key:哈希找到节点,删除原位置,移动到链表头部;
- put 新增 key:容量未满直接插入头部;容量满,删除链表尾部节点,同时删除 HashMap 映射;
- put 更新 key:更新 value,节点移至头部。
2. Redis 近似 LRU 底层实现(区别于标准 LRU)
Redis 没有采用全局双向链表(大量节点移动会阻塞主线程),采用轻量化近似 LRU:
- 存储标记:每个
redisObject对象有 24 位lru字段,记录 key 最后一次访问时间戳; - 淘汰逻辑:触发淘汰时,随机采样 N 个 key(配置
maxmemory-samples,默认 5),对比时间戳,淘汰采样中最久未访问的 key; - 优势:无需维护全局链表,无频繁节点移动,性能损耗极低;
- 缺点:随机采样存在局部最优,无法保证全局真正最久未访问 key 被淘汰,采样数量越大精度越高。
3. Redis LFU 底层实现(4.0 版本新增,优化 LRU 缺陷)
LRU 缺陷:曾经高频、长期不访问的旧热点会长期占用内存(缓存污染);LFU 通过访问频率 + 时间衰减解决。
- 复用 redisObject 的 24 位 lru 字段拆分:
- 高 16 位:记录上次频率衰减的分钟时间;
- 低 8 位:访问频率计数器(Morris 概率计数器,节省内存);
- 计数器规则:每次访问有概率自增,不会无限上涨;
- 衰减机制:配置
lfu-decay-time,长时间未访问自动降低计数器,淘汰历史冷热点。
4. volatile-ttl 实现逻辑
仅筛选设置了过期时间的 key,读取 key 剩余 TTL 值,随机采样后淘汰剩余时间最小的 key,不关注访问行为。
四、延伸问题
-
过期删除和内存淘汰的执行顺序?
写入数据内存超限 → 先尝试清理已过期 key;若内存仍不足,执行内存淘汰策略删除冷热 key。
-
为什么 Redis 不用标准 LRU 双向链表实现?
全局双向链表每次访问都要移动节点,高并发海量 key 场景下大量链表操作阻塞主线程,大幅降低 Redis 吞吐。
-
LRU 和 LFU 怎么选?
- 访问冷热随时间快速切换(短期活动缓存):LRU;
- 长期稳定热点、需要过滤一次性临时访问数据(资讯、推荐缓存):LFU。
-
maxmemory-samples 参数作用?
控制 LRU/LFU 淘汰时随机采样 key 的数量,默认 5;数值越大淘汰算法越精准,但 CPU 开销小幅上升。

浙公网安备 33010602011771号