物理存储:expires 字典
当你为一个 Key 设置了过期时间(如 EXPIRE key 3600),Redis 内部并不会修改该 Key 的原有结构,而是将其放入一个专属的过期字典中。
- 数据结构 :在每个 Redis 数据库(
redisDb)中,都有一个名为expires的dict(字典)。 - Key :指向原始数据库
dict中同一个 SDS 对象的指针(为了省内存,这里不会复制字符串)。 - Value :一个
long long类型的整数,存储的是该 Key 的绝对过期时间戳(毫秒级 Unix 时间戳)。 - 注意:过期字典存储的不是已经过期的数据,而是设置了过期时间的数据
3种常见过期删除策略
1. 定时删除 (Scheduled / Timed Deletion)
- 物理动作 :在设置 Key 过期的同时,创建一个定时器(Timer)。当时间一到,定时器立即触发回调函数,物理抹除该数据。
- CPU 影响 :极高。想象一下如果有 100 万个 Key 同时过期,Redis 就得瞬间启动 100 万个回调。在 Redis 这种单线程模型里,这会直接把主线程卡死,导致无法处理正常的读写请求。
- 内存影响 :极低。一旦过期立刻释放,内存利用率最高。
- 现状 :Redis 并没有采用这种策略。因为它对 CPU 太不友好了,完全违背了高性能缓存的初衷。
2. 惰性删除 (Lazy Deletion)
- 物理动作 :Redis 从不主动检查 Key 是否过期。只有当你执行
GET、SET等命令触碰到某个 Key 时,Redis 才会"顺便"检查它的过期字典。 - 判断逻辑 :如果发现已过期,原地处决(删除),并返回
nil;如果没过期,正常返回数据。 - CPU 影响 :极低。它把删除的压力摊销到了每一次业务请求中,没有额外的扫描开销。
- 内存影响 :有风险 。如果大量 Key 过期后从此再无人问津(冷数据),它们会一直霸占物理内存,造成严重的内存泄漏。
3. 定期删除 (Periodic Deletion)
- 物理动作 :Redis 内部的
serverCron任务会每秒执行 10 次(默认频率),调用activeExpireCycle函数对expires字典进行随机抽样检查。 - 算法逻辑 (25% 准则) :
- 从过期字典中随机抽取 20 个 Key。
- 删除其中已过期的 Key。
- 循环触发 :如果这 20 个 Key 中过期的比例超过 25%,说明过期 Key 还是很密集,立即再次执行步骤 1。
- 熔断机制:为了防止扫描太久导致主线程卡顿,单次扫描有严格的时间上限(通常是 25ms)。
- 物理权衡:通过限制删除操作执行的时长和频率,来减少对 CPU 的占用,同时又能有效释放内存。
💡
Redis 的物理方案 = 惰性删除 + 定期删除
这是一个互补的闭环:
- 定期删除 负责在大规模数据中进行"地毯式巡逻",消灭大部分过期的冷数据。
- 惰性删除 负责作为"临检岗",拦截那些漏网之鱼,保证用户绝对不会读到过期的数据。