Redis过期键删除策略深度解析
在Redis的实际应用中,缓存过期、内存占用过高是开发和运维过程中高频遇到的问题,而过期键删除策略是Redis解决该问题的核心机制。合理的过期键删除策略能在CPU资源占用和内存利用率之间找到最佳平衡,也是Redis高性能的重要保障。本文将从过期键的基础操作、核心删除策略、Redis实际实现、特殊场景处理等方面全面解析。
一、基础铺垫:Redis过期键的设置与管理
要理解过期键删除策略,首先需要掌握Redis中过期键的基本操作,以及Redis底层如何存储过期键信息。
1.1 过期时间的设置与查询
Redis提供了多种命令为key设置过期时间,同时支持查询剩余存活时间、取消过期时间等操作,覆盖秒/毫秒、时间戳等多种维度。
| 设置过期(秒) | expire key n | 设key在n秒后过期 | expire user:1 60 |
| 设置过期(毫秒) | pexpire key n | 设key在n毫秒后过期 | pexpire user:1 60000 |
| 时间戳过期(秒) | expireat key n | 设key在秒级时间戳n后过期 | expireat user:1 1736179200 |
| 时间戳过期(毫秒) | pexpireat key n | 设key在毫秒级时间戳n后过期 | pexpireat user:1 1736179200123 |
| 设值同时过期(秒) | set key value ex n | 原子操作,设值并指定秒级过期 | set session 123 ex 60 |
| 设值同时过期(秒) | setex key n value | 专用命令,等价于上述ex操作 | setex token 300 abc123 |
| 查询剩余时间 | ttl key | 返回剩余秒数,-1永不过期,-2已过期/不存在 | ttl user:1 → (integer)56 |
| 取消过期时间 | persist key | 移除key的过期时间,变为永久有效 | persist user:1 → (integer)1 |
1.2 过期字典:Redis底层的过期键存储
当为key设置过期时间后,Redis会将该key的过期信息存储在过期字典(expires dict) 中,该字典是Redis数据库结构redisDb的核心组成部分。
底层结构定义如下:
typedef struct redisDb{
dict *dict; // 存放数据库所有键值对
dict *expires; // 过期字典,存放key的过期时间
// 其他属性…
}redisDb;
过期字典的核心特征:
- key:指向Redis中键对象的指针,与dict中的key一一对应;
- value:long long型整数,存储key的毫秒级Unix时间戳;
- 时间复杂度:基于哈希表实现,查询、新增、删除的时间复杂度均为O(1)。
Redis判断一个key是否过期的逻辑非常简单:
二、三大核心过期键删除策略
业界主流的过期键删除策略分为定时删除、惰性删除、定期删除三种,三者的核心差异在于删除时机和资源占用倾向,分别对应“内存优先”、“CPU优先”、“折中平衡”三种设计思路。
2.1 定时删除:内存友好,CPU不友好
核心原理
在为key设置过期时间的同时,创建一个定时事件,当时间到达过期点时,由Redis事件处理器自动执行该key的删除操作。
执行流程图
设置key过期时间 → 创建定时事件(绑定过期时间和删除操作)→ 时间到达 → 事件处理器执行删除 → key被清理
优缺点
- ✅ 优点:过期key被立即删除,无内存无效占用,对内存利用率最友好;
- ❌ 缺点:若存在大量过期key,会创建大量定时事件,频繁触发删除操作,占用大量CPU资源,严重影响Redis的核心读写性能。
适用场景
几乎无实际适用场景,Redis未采用该策略。
2.2 惰性删除:CPU友好,内存可能浪费
核心原理
Redis不主动检测和删除过期key,只有当客户端访问该key时,才会检查其是否过期;若过期则立即删除,未过期则正常返回键值对。
执行流程图
客户端发起key访问请求 → Redis调用expireIfNeeded函数检查过期状态 → 未过期→返回键值对
↓ 过期
删除key(同步/异步)→ 返回null给客户端
优缺点
- ✅ 优点:仅在访问时执行检查,几乎不占用额外CPU资源,实现简单,对CPU最友好;
- ❌ 缺点:若过期key长期不被访问,会一直占用内存,造成内存泄漏风险,极端情况下会导致Redis内存爆满。
关键实现
Redis通过核心函数expireIfNeeded实现惰性删除,Redis4.0+支持通过lazyfree_lazy_expire参数配置同步删除或异步删除,避免删除大key时阻塞主线程。
核心代码片段:
int expireIfNeeded(redisDb *db, robj *key) {
mstime_t when = getExpire(db, key);
mstime_t now = mstime();
if (when < 0) return 0; // 无过期时间,直接返回
if (now <= when) return 0; // 未过期,直接返回
// 主从模式下,从库不主动删除,交给主库处理
if (server.masterhost != NULL) return 1;
server.stat_expiredkeys++; // 统计过期删除的key数量
// 根据配置选择同步/异步删除
if (server.lazyfree_lazy_expire)
dbAsyncDelete(db, key); // 异步删除
else
dbSyncDelete(db, key); // 同步删除
notifyKeyspaceEvent(NOTIFY_EXPIRED, "expired", key, db->id);
return 1;
}
2.3 定期删除:折中方案,平衡CPU与内存
核心原理
Redis每隔一段时间,随机从过期字典中抽取一定数量的key进行过期检查,删除其中的过期key;同时通过限制执行时长和频率,避免对CPU造成过度占用。
执行流程图
Redis主线程触发定期检查(由serverCron调度)→ 从过期字典随机抽取20个key → 检查并删除过期key
→ 统计过期key占比 → 占比>25%→重复抽样删除 ↓ 占比≤25%/执行时间≥25ms
结束本轮定期删除
核心配置与规则
定期删除的执行逻辑由Redis底层调度器serverCron控制,核心规则和配置如下:
优缺点
- ✅ 优点:通过频率、数量、时间三重限制,平衡了CPU占用和内存浪费,是定时删除和惰性删除的折中方案;
- ❌ 缺点:随机抽样可能导致部分过期key未被检测到,仍会存在一定的内存无效占用;且hz参数需要根据业务场景精细配置,配置不当会影响效果。
三、Redis的最终选择:惰性删除+定期删除组合策略
Redis并未选择单一的删除策略,而是采用惰性删除+定期删除的组合策略,充分发挥两者的优点,规避各自的缺点,在合理使用CPU时间和尽量避免内存浪费之间取得最佳平衡。
3.1 组合策略整体执行流程图
Redis启动 → 初始化过期字典,维护key过期信息
├─ 客户端访问key → 惰性删除:执行expireIfNeeded检查,过期则删除
└─ 后台定期调度(hz控制频率)→ 定期删除:随机抽样检查,删除过期key
↓ 内存占用持续升高 → 触发内存淘汰策略(maxmemory控制)
3.2 组合策略的核心优势
四、特殊场景:过期键的处理规则
在Redis的主从模式和持久化(RDB/AOF) 场景下,过期键的处理有特殊规则,这是开发和运维中容易踩坑的点,必须重点掌握。
4.1 主从模式下的过期键处理
主从模式中,从库不主动执行过期键删除操作,所有过期键的删除由主库统一控制,保证主从数据一致性:
4.2 持久化场景下的过期键处理
过期键在RDB和AOF两种持久化文件中的处理逻辑不同,分为生成/写入阶段和加载/重写阶段。
RDB文件中的过期键
- 生成阶段:持久化生成新RDB文件时,Redis会对key进行过期检查,过期key不会被写入RDB文件;
- 加载阶段:主库加载RDB时,会过滤过期key;从库加载RDB时,会载入所有key(包括过期key),但主从同步后会被清理。
AOF文件中的过期键
- 写入阶段:过期key未被删除时,AOF文件会保留其相关记录;当过期key被删除后,Redis会向AOF文件追加del指令,标记该key的删除;
- 重写阶段:执行AOF重写时,Redis会检查内存中的键值对,过期key不会被写入重写后的AOF文件。
五、延伸:过期删除策略与内存淘汰策略的配合
当Redis的运行内存达到maxmemory设置的上限时,仅靠过期删除策略无法解决内存问题,此时会触发内存淘汰策略,删除符合条件的key以释放内存。过期删除策略处理已过期的key,而内存淘汰策略处理内存超限后的key,两者配合构成Redis的完整内存管理体系。
5.1 内存淘汰策略的分类(Redis4.0+共8种)
Redis的内存淘汰策略分为三大类,可通过maxmemory-policy参数配置:
5.2 核心推荐配置
- 通用业务场景:allkeys-lru,淘汰所有key中最近最少使用的,适配绝大多数缓存场景;
- 热点数据明显场景:allkeys-lfu,淘汰访问频率最低的key,更贴合热点数据分布;
- 数据不可丢失场景:noeviction,拒绝写操作,避免数据丢失;
- 部分数据持久化场景:volatile-lru,仅淘汰带过期时间的缓存key,保护持久化数据。
5.3 实战配置命令
# 临时设置最大内存为512MB
CONFIG SET maxmemory 536870912
# 临时设置内存淘汰策略为allkeys-lru
CONFIG SET maxmemory-policy allkeys-lru
# 持久化配置(写入redis.conf)
CONFIG REWRITE
六、实战配置与性能优化建议
结合过期键删除策略的原理,给出以下实战配置和性能优化建议,适配大多数生产环境:
七、总结
Redis的过期键删除策略是其内存管理的核心,核心要点可总结为以下3点:
网硕互联帮助中心








评论前必须登录!
注册