从零起步学习Redis || 第八章:过期删除策略与内存淘汰策略详解及实战使用(LRU和LFU算法详解)
一、前言
在日常开发中,Redis 凭借其高性能和丰富的数据结构,成为后端缓存系统的首选。然而,随着数据量不断增加,内存占用与数据过期管理 成为必须关注的问题。
本文将系统讲解 Redis 的两大核心机制:
-
过期删除策略(Expiration Deletion Policy)
-
内存淘汰策略(Eviction Policy)
二、Redis 过期删除策略
1. 如何设置过期时间
Redis 提供多种方式为 Key 设置过期时间(TTL,Time To Live):
命令 | 说明 | 示例 |
---|---|---|
expire key seconds | 设置生存时间(秒) | expire user:1 60 |
pexpire key milliseconds | 设置生存时间(毫秒) | pexpire user:1 1500 |
expireat key timestamp | 设置过期时间(秒级时间戳) | expireat user:1 1736179200 |
pexpireat key milliseconds-timestamp | 设置过期时间(毫秒级时间戳) | pexpireat user:1 1736179200123 |
set key value ex seconds | 设置值并指定过期时间 | set session 123 ex 60 |
查看 TTL:
ttl key
返回:
-
-1
→ 永不过期 -
-2
→ 已过期或不存在
2. Redis 如何判断 Key 是否过期
Redis 内部为每个带有过期时间的 key 维护一个 过期字典(expires),保存该 key 的过期时间戳。
判断逻辑:
当访问一个 key 时,如果当前系统时间 > 过期时间戳,则该 key 被标记为过期。
3.过期字典是如何实现的?
Redis 在服务器内部维护着两个主要的全局字典:
字典名 | 作用 |
---|---|
db->dict | 保存所有的 键值对(key → value) |
db->expires | 保存所有 带过期时间的键(key → 过期时间戳) |
也就是说,一个 key 如果设置了过期时间,会在两个地方出现:
-
db->dict
→ 保存 key 对应的值; -
db->expires
→ 保存 key 的过期时间。
📌 key 只有在设置了 TTL 时才会出现在 expires 字典中。
Redis 的 expires
是一个 哈希表(dict),与普通的 db->dict
类似。
其结构类似于:
dict *expires; // 保存过期时间的字典
内部数据(伪结构):
expires = {"user:1" -> 1736200000000, // 毫秒级时间戳"session:abc" -> 1736200050000,"token:xyz" -> 1736200100000
}
Redis 以 毫秒时间戳 记录过期时间(使用 mstime()
获取当前时间)。
4. Redis 三种过期删除策略
策略 | 说明 | 优点 | 缺点 |
---|---|---|---|
惰性删除(Lazy Deletion) | 访问 key 时检查是否过期,若过期则删除。 | 减少 CPU 消耗 | 可能堆积过期 key,占内存 |
定期删除(Periodic Deletion) | Redis 每秒10次 抽样检测20个 key(源码中写死了) 是否过期并删除。 | 控制内存增长 | 占用部分 CPU |
混合策略(实际采用) | Redis 同时使用惰性删除 + 定期删除。 | 性能与内存平衡最佳 | — |
还有一个定时删除策略:在设置key的过期时间时还会设置一个定时事件,当时间达到后,事件来控制key的删除。
注意,在定期删除中,如果本轮检查的 已过期 key 的数量 超过5个(20*0.25),也就是「已过期 key 的数量」占比「随机抽取 key 的数量」大于 25%,则继续抽样,即重复执行删除策略;如果已过期的 key 比例小于 25%,则停止继续删除过期 key,然后等待下一轮再检查。
那 Redis 为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过 25ms。
🧠 总结一句话:
Redis 通过 “惰性 + 定期” 的混合删除策略,在性能与内存之间取得平衡。
三、Redis 内存淘汰策略
当 Redis 的内存使用达到上限(maxmemory
)时,会触发内存淘汰机制。
1. 设置 Redis 最大内存
配置文件 redis.conf
:
maxmemory 512mb
或命令行动态设置:
config set maxmemory 512mb
查看配置:
config get maxmemory
2. Redis 内存淘汰策略列表
通过以下配置设置:
maxmemory-policy <policy>
策略 | 说明 | 适用场景 |
---|---|---|
noeviction | 不淘汰,写操作报错 | 默认值,不建议用于缓存系统 |
volatile-lru | 在带过期时间的键中,淘汰最近最少使用的 | 临时数据场景 |
volatile-lfu | 在带过期时间的键中,淘汰使用次数最少的 | Redis 4.0+,热点数据 |
volatile-ttl | 在带过期时间的键中,优先淘汰即将过期的 | 对 TTL 敏感的业务 |
volatile-random | 在带过期时间的键中随机淘汰 | 特殊需求 |
allkeys-lru | 在所有键中淘汰最近最少使用的 | 常见缓存策略 |
allkeys-lfu | 在所有键中淘汰使用频率最低的 | 高频访问系统 |
allkeys-random | 在所有键中随机删除 | 简单但不推荐 |
推荐策略:
-
缓存系统 →
allkeys-lru
或allkeys-lfu
-
仅缓存临时数据 →
volatile-lru
四、LRU 与 LFU 算法解析
1. LRU(Least Recently Used)
-
定义:淘汰最近最少使用的 Key。
-
核心思想:如果一个 Key 很久没被访问,说明它可能不再重要。
-
Redis 实现:使用近似 LRU 算法,通过访问时间采样实现。在Redis的对象结构体中添加了一个字段:记录此数据最后一次的访问时间(lru)
2. LFU(Least Frequently Used)
-
定义:淘汰一段时间内访问次数最少的 Key。
-
核心思想:被频繁访问的数据应更“珍贵”。
-
Redis 4.0+ 支持 LFU,通过访问计数器和衰减算法实现。在Redis的对象结构体中添加了两个字段:记录此数据最后一次的访问时间和访问频次
配置项:
lfu-log-factor 10
lfu-decay-time 1
下图内容来源于:小林codingRedis 过期删除策略和内存淘汰策略有什么区别? | 小林coding | Java面试学习
3. LRU vs LFU 对比表
特性 | LRU | LFU |
---|---|---|
依据 | 最近访问时间 | 访问频率 |
优点 | 简单高效 | 更智能,识别真正热点 |
缺点 | 忽略访问频率 | 实现复杂度更高 |
适合场景 | 时间局部性强(短期热点) | 稳定长期热点 |
Redis 支持版本 | 所有版本 | 4.0+ |
五、总结
内容 | 核心要点 |
---|---|
过期删除策略 | 惰性删除 + 定期删除(混合策略) |
内存淘汰条件 | 内存达到 maxmemory 限制 |
内存淘汰策略 | LRU、LFU、TTL、RANDOM 等 |
推荐策略 | 缓存场景使用 allkeys-lfu 或 allkeys-lru |
算法区别 | LRU:最近最少使用;LFU:最少使用频率 |
六、Java 后端开发实战建议
在 Spring Boot 项目中使用 Redis 时,可通过配置实现:
spring:data:redis:host: localhostport: 6379timeout: 3000cache:redis:time-to-live: 60s
建议:
-
缓存数据时务必设置 合理的 TTL;
-
根据业务选择合适的淘汰策略;
-
对于热点数据,建议使用 LFU 策略提升命中率;
-
结合 缓存预热 + 更新机制,防止缓存击穿。
七、结语
Redis 的过期删除与内存淘汰机制,是保障缓存系统 稳定性与高性能 的关键。