一、简述
Redis 中的过期 key 到底是如何被清除的?实际上并没有单一的解决方案,而是三种策略共同作用:定时删除、定期删除、惰性删除。

其中,定时删除和定期删除属于主动触发,惰性删除则更像是被动响应。
二、定时删除
这一策略非常直观:在设置 key 过期时间的瞬间,即同时创建一个定时器。一旦时间到达,删除操作立即执行,毫不延迟。
优点:
从内存占用角度来看,定时删除是最高效的方式。过期 key 几乎能够被瞬间清理,内存释放极其及时,几乎不会残留任何脏数据。
缺点:
代价全部集中在 CPU 上。如果过期 key 数量较多,删除操作对 CPU 的消耗会非常显著。试想一下,在服务器本身请求量已很高、CPU 资源紧张的环境下,还要分出部分时间去执行这些“额外”的删除任务,这必然拖慢正常请求的响应速度,降低整体吞吐量。
更深层次的问题在于,Redis 内部的时间事件机制采用无序链表实现。查找一个事件的复杂度为 O(N),这种结构在处理少量事件时尚可,但面对大量定时器时则力不从心。因此,现阶段依靠定时器来实现大量 key 的定时删除,技术上并不现实。
三、定期删除
既然定时删除效率高但消耗 CPU,惰性删除又过于被动,那么折中方案就是“定期”出手。Redis 会按一定时间间隔主动检查数据库,并清理部分过期 key。至于具体删除多少个、检查几个库,都由内部算法动态决定。
关键在于,通过控制删除操作的执行时长和频率,Redis 避免了对 CPU 的过度占用。当然,这两个参数需要设置得恰到好处,否则策略可能偏离预期:设得太密太猛,会退化为定时删除;设得太稀太少,则与惰性删除无异。
优点:
在主动删除与 CPU 消耗之间找到了平衡点,能较为有效地控制过期 key 造成的内存浪费,同时不会严重影响服务器性能。
四、惰性删除
这一策略的哲学是“懒惰”。它不会对过期 key 做任何主动清理。只有当客户端尝试读取某个 key 时,程序才会顺便检查它是否已过期;如果过期,则顺手删除并返回空;如果未过期,则正常返回 value。
优点:
可以说是对 CPU 最友好的策略。所有的过期检查仅在“非做不可”时进行——也就是当你需要获取这个 key 的时候。而且,它只处理当前被访问的 key,绝不会在那些无人问津的过期 key 上浪费任何一个 CPU 周期。
缺点:
代价全部落在内存上。一个 key 虽然过期了,但只要没被访问,它就会一直驻留在内存中。如果数据库中存在大量过期的 key,且恰好长期无人访问,它们就会像垃圾一样永久占用内存。除非用户手动执行 flushdb 清库,否则这些内存永远不会被释放。
因此,Redis 在实际运行中采用的是定期删除 + 惰性删除的组合策略。两者相互配合,才能在 CPU 时间与内存空间之间找到最优平衡点。
五、Redis 中 flushall 和 flushdb 的区别
这两个命令都能“清空数据”,但背后的差异很大:
- flushall :清空后,会触发持久化,RDB 文件也会随之更新(会被重写为初始的 76 字节大小)。因此执行完 flushall 后,数据库彻底清空。
- flushdb :清空当前数据库,但不会触发持久化,RDB 文件保持不变。这就很有意思——Redis 启动时是从 RDB 文件加载数据的。所以清库后,如果直接 kill 掉 Redis-server 进程(注意,不要用 shutdown,因为 shutdown 会触发持久化),然后重新启动,数据就能从保存的 RDB 中完整恢复,仿佛什么都没有发生过。
需要特别留意的是:
不要使用 shutdown,它会触发持久化。正确的恢复方式是先用 lsof -i:6379 找到进程号,然后直接 kill 掉。
总结
从整体来看,Redis 的过期 key 删除策略是一门权衡的艺术——定时删除追求极致的内存管理,但代价是 CPU;惰性删除把 CPU 放在第一位,但可能造成内存泄漏;而定期删除则在两者之间寻找平衡。理解这三者的关系及其各自的代价,才能在真实业务场景中更好地配置和使用 Redis。
