Redis过期删除与内存淘汰策略详解

0 阅读9分钟

在Redis的使用过程中,过期删除策略内存淘汰策略是两个核心机制,直接关系到Redis的内存使用效率和系统稳定性。今天我们就来详细聊聊这两个话题,一文帮你彻底搞懂!

image.png

一、过期删除策略

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会:

  1. 在expires字典中查找该key
  2. 如果找到,获取其过期时间
  3. 将过期时间与当前系统时间比较
  4. 如果当前时间 > 过期时间,则判定为过期

3. 过期删除策略有哪些?

在讨论Redis的具体策略之前,我们先了解常见的过期删除策略:

策略工作原理优点缺点
定时删除设置key过期时间时,创建一个定时器,到期立即删除内存友好,及时释放空间CPU不友好,大量key会占用大量CPU资源
惰性删除每次访问key时检查是否过期,过期则删除CPU友好,只检查被访问的key内存不友好,过期key可能长时间占用内存
定期删除每隔一段时间,随机检查一部分key,删除过期key折中方案,平衡CPU和内存难以确定合适的执行频率

4. Redis过期删除策略是什么?

Redis采用的是惰性删除 + 定期删除的组合策略。

(1)惰性删除

当客户端访问一个key时,Redis会先检查该key是否在过期字典中:

  • 如果已过期,删除该key,然后返回nil
  • 如果未过期,正常返回数据

代码逻辑伪代码:

if (key 已过期) {
    delete key;
    return null;
} else {
    return key.value;
}

优点:对CPU友好,只检查被访问的key 缺点:如果key过期后一直不被访问,会一直占用内存

(2)定期删除

Redis会每隔一段时间(默认100ms)执行一次过期key清理,具体流程:

  1. 从所有数据库中随机抽取20个设置了过期时间的key
  2. 检查这20个key,删除其中已过期的key
  3. 如果过期key的比例超过25%(即删除了5个以上),则重复步骤1
  4. 否则,本次清理结束,等待下一次清理

配置参数:

  • 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)

# 设置最大内存为2GB
maxmemory 2gb

# 或者设置字节数
maxmemory 2097152000

(2)命令行方式(运行时修改)

# 设置最大内存为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+)缓存场景,有明确热点

如何设置淘汰策略?

# 配置文件设置
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。

示例

访问顺序:AB → C → D → B
当前缓存:[D, B, C](假设容量为3)
新访问E时:淘汰A(最久未访问)

Redis中的近似LRU

  • Redis并没有实现标准的LRU(需要维护双向链表,占用额外内存)
  • 而是采用近似LRU:随机抽样,淘汰其中空闲时间最长的
  • 通过maxmemory-samples配置抽样数量(默认5),越大越接近真实LRU
# 设置抽样数量
CONFIG SET maxmemory-samples 10

LFU(Least Frequently Used)最不经常使用

设计原则:如果一个数据最近被访问的次数多,那么之后被访问的概率也会越大。

实现方式:记录每个key的访问频率,淘汰时选择访问频率最低的key。

示例

访问统计:A(100次) B(50次) C(30次) D(10次)
当前缓存:[A, B, C]
新访问E时:淘汰D(频率最低)

Redis中的LFU实现

  • 每个key维护一个24bit的计数器
  • 计数器不是简单的累加,而是对数递增,越往后增长越慢
  • 同时计数器会随时间衰减,避免长期热点永不淘汰
  • 通过lfu-decay-timelfu-log-factor调整行为
# 设置计数器衰减时间(分钟)
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种淘汰策略可选

配置建议

  1. 生产环境一定要设置maxmemory,避免内存溢出
  2. 根据业务特点选择合适的淘汰策略
    • 通用场景:allkeys-lru
    • 明确热点场景:allkeys-lfu
    • 缓存场景:volatile-lru
  3. 调整抽样数量maxmemory-samples)平衡精度和性能
  4. 监控命中率,适时调整策略

监控命令

# 查看内存使用情况
INFO memory

# 查看命中率
INFO stats | grep keyspace

# 查看淘汰key数量
INFO stats | grep evicted_keys

本文总结:Redis的过期删除策略(惰性删除+定期删除)和内存淘汰策略(8种策略)共同构成了Redis高效的内存管理机制。理解这两大机制的原理和配置,对于保障Redis稳定运行、提高缓存效率至关重要。希望本文能帮助大家全面掌握Redis的内存管理核心知识!


欢迎点赞、收藏、转发,让更多小伙伴看到!