在Redis的使用过程中,过期删除策略 和内存淘汰策略是两个核心机制,直接关系到Redis的内存使用效率和系统稳定性。今天我们就来详细聊聊这两个话题,一文帮你彻底搞懂!
一、过期删除策略
1. 如何设置过期时间?
Redis提供了多种命令来设置key的过期时间:
| 命令 | 说明 | 示例 |
|---|---|---|
EXPIRE key seconds |
设置key在seconds秒后过期 | EXPIRE name 60(60秒后过期) |
PEXPIRE key milliseconds |
设置key在milliseconds毫秒后过期 | PEXPIRE name 60000(60000毫秒后过期) |
EXPIREAT key timestamp |
设置key在指定的时间戳(秒)过期 | EXPIREAT name 1735689600(2025-01-01 00:00:00过期) |
PEXPIREAT key milliseconds-timestamp |
设置key在指定的时间戳(毫秒)过期 | PEXPIREAT name 1735689600000 |
SETEX key seconds value |
设置字符串key的同时指定过期时间 | SETEX name 60 "zhangsan" |
PSETEX key milliseconds value |
设置字符串key的同时指定毫秒级过期时间 | PSETEX name 60000 "zhangsan" |
查看剩余过期时间:
TTL key:查看key剩余的过期时间(秒)PTTL key:查看key剩余的过期时间(毫秒)
取消过期时间:
PERSIST key:移除key的过期时间,使其永不过期
2. 如何判定key已过期?
Redis内部维护了一个名为**expires的字典**(也称为过期字典),专门用来保存设置了过期时间的key及其过期时间。
过期字典的结构:
- key:指向实际存储的key对象
- value :一个
long long类型的整数,保存了key的过期时间(毫秒级时间戳)
判定逻辑: 当需要判断一个key是否过期时,Redis会:
- 在expires字典中查找该key
- 如果找到,获取其过期时间
- 将过期时间与当前系统时间比较
- 如果当前时间 > 过期时间,则判定为过期
3. 过期删除策略有哪些?
在讨论Redis的具体策略之前,我们先了解常见的过期删除策略:
| 策略 | 工作原理 | 优点 | 缺点 |
|---|---|---|---|
| 定时删除 | 设置key过期时间时,创建一个定时器,到期立即删除 | 内存友好,及时释放空间 | CPU不友好,大量key会占用大量CPU资源 |
| 惰性删除 | 每次访问key时检查是否过期,过期则删除 | CPU友好,只检查被访问的key | 内存不友好,过期key可能长时间占用内存 |
| 定期删除 | 每隔一段时间,随机检查一部分key,删除过期key | 折中方案,平衡CPU和内存 | 难以确定合适的执行频率 |
4. Redis过期删除策略是什么?
Redis采用的是惰性删除 + 定期删除的组合策略。
(1)惰性删除
当客户端访问一个key时,Redis会先检查该key是否在过期字典中:
- 如果已过期,删除该key,然后返回nil
- 如果未过期,正常返回数据
代码逻辑伪代码:
c
if (key 已过期) {
delete key;
return null;
} else {
return key.value;
}
优点 :对CPU友好,只检查被访问的key 缺点:如果key过期后一直不被访问,会一直占用内存
(2)定期删除
Redis会每隔一段时间(默认100ms)执行一次过期key清理,具体流程:
- 从所有数据库中随机抽取20个设置了过期时间的key
- 检查这20个key,删除其中已过期的key
- 如果过期key的比例超过25%(即删除了5个以上),则重复步骤1
- 否则,本次清理结束,等待下一次清理
配置参数:
hz:serverCron任务的执行频率,默认10,即每秒执行10次,每次清理时间约25ms- 可以通过
CONFIG SET hz 20调整,增大频率会提高清理速度,但会增加CPU负担
定期删除的特点:
- 不会遍历所有key,而是随机抽样,避免长时间阻塞
- 自适应:如果过期比例高,会持续清理
- CPU和内存的平衡:通过参数可调
为什么采用组合策略?
| 策略 | 解决的问题 | 不足之处 | 互补关系 |
|---|---|---|---|
| 惰性删除 | 避免无谓的CPU消耗 | 过期key可能长期占用内存 | 定期删除清理这些key |
| 定期删除 | 主动清理过期key,释放内存 | 执行频率不好确定 | 惰性删除作为补充 |
两者结合:Redis既保证了CPU的高效利用,又避免了过期key长期占用内存,是一种非常精妙的设计。
二、内存淘汰策略
当Redis内存使用达到上限时,就需要通过内存淘汰策略来腾出空间存储新数据。
1. 如何设置Redis最大运行内存?
(1)配置文件方式(redis.conf)
conf
# 设置最大内存为2GB
maxmemory 2gb
# 或者设置字节数
maxmemory 2097152000
(2)命令行方式(运行时修改)
bash
# 设置最大内存为2GB
CONFIG SET maxmemory 2gb
# 查看当前设置
CONFIG GET maxmemory
(3)不设置maxmemory会怎样?
- 在64位系统中,maxmemory默认为0,表示不限制内存使用
- 如果不限制,Redis会一直使用内存直到耗尽系统所有内存,可能导致系统OOM(Out of Memory)
- 生产环境强烈建议设置maxmemory,并配置合理的淘汰策略
2. Redis内存淘汰策略有哪些?
Redis提供了多种内存淘汰策略,主要分为三大类:
| 类别 | 策略 | 说明 | 适用场景 |
|---|---|---|---|
| 不淘汰 | noeviction |
内存不够时直接返回错误(默认策略) | 不允许丢失数据的场景 |
| 所有key中淘汰 | allkeys-random |
从所有key中随机挑选淘汰 | 访问概率均匀的场景 |
allkeys-lru |
淘汰最近最少使用的key | 有热点数据的场景 | |
allkeys-lfu |
淘汰使用频率最低的key(4.0+) | 有明确热点的场景 | |
| 过期key中淘汰 | volatile-random |
从设置了过期时间的key中随机挑选淘汰 | 缓存场景 |
volatile-lru |
淘汰最近最少使用的过期key | 缓存场景,有热点 | |
volatile-ttl |
淘汰快要过期的key | 缓存场景,优先淘汰即将过期的 | |
volatile-lfu |
淘汰使用频率最低的过期key(4.0+) | 缓存场景,有明确热点 |
如何设置淘汰策略?
bash
# 配置文件设置
maxmemory-policy allkeys-lru
# 命令行设置
CONFIG SET maxmemory-policy allkeys-lru
# 查看当前策略
CONFIG GET maxmemory-policy
各策略的优缺点对比
| 策略 | 优点 | 缺点 | 推荐指数 |
|---|---|---|---|
noeviction |
数据不丢失 | 写入可能失败 | ⭐⭐ |
allkeys-lru |
保留热点数据 | 实现稍复杂 | ⭐⭐⭐⭐⭐ |
allkeys-lfu |
精准识别热点 | 需要统计频率 | ⭐⭐⭐⭐ |
volatile-ttl |
优先淘汰快过期的 | 需要设置过期时间 | ⭐⭐⭐ |
allkeys-random |
实现简单 | 可能淘汰热点 | ⭐⭐ |
3. LRU算法和LFU算法有什么区别?
LRU(Least Recently Used)最近最少使用
设计原则:如果一个数据近期没有被访问,那么之后一段时间也不会被访问。
实现方式 :记录每个key的最后访问时间,淘汰时选择最久没被访问的key。
示例:
css
访问顺序:A → B → C → D → B
当前缓存:[D, B, C](假设容量为3)
新访问E时:淘汰A(最久未访问)
Redis中的近似LRU:
- Redis并没有实现标准的LRU(需要维护双向链表,占用额外内存)
- 而是采用近似LRU:随机抽样,淘汰其中空闲时间最长的
- 通过
maxmemory-samples配置抽样数量(默认5),越大越接近真实LRU
bash
# 设置抽样数量
CONFIG SET maxmemory-samples 10
LFU(Least Frequently Used)最不经常使用
设计原则:如果一个数据最近被访问的次数多,那么之后被访问的概率也会越大。
实现方式 :记录每个key的访问频率,淘汰时选择访问频率最低的key。
示例:
scss
访问统计:A(100次) B(50次) C(30次) D(10次)
当前缓存:[A, B, C]
新访问E时:淘汰D(频率最低)
Redis中的LFU实现:
- 每个key维护一个24bit的计数器
- 计数器不是简单的累加,而是对数递增,越往后增长越慢
- 同时计数器会随时间衰减,避免长期热点永不淘汰
- 通过
lfu-decay-time和lfu-log-factor调整行为
bash
# 设置计数器衰减时间(分钟)
CONFIG SET lfu-decay-time 1
# 设置对数因子,影响计数器增长速度
CONFIG SET lfu-log-factor 10
LRU和LFU的对比
| 对比维度 | LRU算法 | LFU算法 |
|---|---|---|
| 淘汰依据 | 最近访问时间 | 访问频率 |
| 实现复杂度 | 相对简单 | 相对复杂 |
| 内存占用 | 需要记录访问时间 | 需要计数器 |
| 热点识别 | 只能识别近期热点 | 能识别长期热点 |
| 适用场景 | 访问模式随时间变化 | 热点相对固定 |
| 缺点 | 突发流量可能污染缓存 | 计数器需要衰减机制 |
选型建议
| 业务场景 | 推荐策略 | 原因 |
|---|---|---|
| 电商大促、新闻热点 | allkeys-lru |
热点随时间变化,LRU更合适 |
| 长尾数据、固定热点 | allkeys-lfu |
某些key长期高访问,LFU更准确 |
| 缓存场景(可重建) | volatile-* |
只淘汰有TTL的key,避免丢失持久数据 |
| 数据不能丢 | noeviction |
严格控制,宁愿失败也不丢数据 |
总结
过期删除策略 vs 内存淘汰策略
| 对比项 | 过期删除策略 | 内存淘汰策略 |
|---|---|---|
| 触发条件 | key到达过期时间 | 内存达到maxmemory上限 |
| 作用对象 | 设置了TTL的key | 所有key(取决于策略) |
| 执行时机 | 访问时(惰性)、定时(定期) | 写入新数据时 |
| 核心目的 | 释放过期key占用的内存 | 腾出空间存新数据 |
| Redis策略 | 惰性删除 + 定期删除 | 8种淘汰策略可选 |
配置建议
- 生产环境一定要设置
maxmemory,避免内存溢出 - 根据业务特点选择合适的淘汰策略 :
- 通用场景:
allkeys-lru - 明确热点场景:
allkeys-lfu - 缓存场景:
volatile-lru
- 通用场景:
- 调整抽样数量 (
maxmemory-samples)平衡精度和性能 - 监控命中率,适时调整策略
监控命令
bash
# 查看内存使用情况
INFO memory
# 查看命中率
INFO stats | grep keyspace
# 查看淘汰key数量
INFO stats | grep evicted_keys
本文总结:Redis的过期删除策略(惰性删除+定期删除)和内存淘汰策略(8种策略)共同构成了Redis高效的内存管理机制。理解这两大机制的原理和配置,对于保障Redis稳定运行、提高缓存效率至关重要。希望本文能帮助大家全面掌握Redis的内存管理核心知识!
欢迎点赞、收藏、转发,让更多小伙伴看到!
