Redis 键过期删除策略
本文讲解Redis的过期策略:通过结合“惰性删除”与“定期删除”,在CPU和内存开销之间取得平衡,高效地管理过期键。
我们来深入探讨 Redis 的过期策略。这是一个非常核心且设计精巧的机制,它决定了 Redis 如何管理带有生存时间(TTL)的键。
简单来说,Redis 并不会在键过期的一瞬间就立即删除它,而是采用了一种组合策略来在性能和内存使用之间取得平衡。
Redis 的过期删除策略是:定期删除 + 惰性删除。
1. 惰性删除 (Lazy Expiration / Passive Deletion)
这是最直接也最简单的策略。
工作方式:当客户端尝试访问一个键时(例如使用
GET,HGET等命令),Redis 会首先检查这个键是否已经设置了过期时间,并且是否已经过期。- 如果已过期:Redis 会立即删除这个键,然后向客户端返回
nil(空),就好像这个键从来不存在一样。 - 如果未过期:正常执行命令并返回结果。
- 如果已过期:Redis 会立即删除这个键,然后向客户端返回
优点:
- 对 CPU 非常友好:删除操作只在键被访问时发生,不会在其他时间浪费 CPU 资源去检查和删除。
缺点:
- 可能导致内存浪费:如果一个键设置了过期时间,但之后再也没有被访问过,那么它就会一直“躺”在内存里,直到 Redis 实例重启。如果这种“僵尸”键很多,会造成严重的内存泄漏。
显然,只靠惰性删除是远远不够的。
2. 定期删除 (Active Expiration / Periodic Deletion)
为了弥补惰性删除的缺点,Redis 引入了定期删除策略。
工作方式:Redis 内部有一个定时任务(在
serverCron函数中执行),默认每秒执行 10 次(即每 100 毫秒一次)。这个任务会执行以下操作:- 从设置了过期时间的键集合中,随机抽取一部分键(默认是 20 个)。
- 检查这些被抽中的键是否已过期。
- 如果已过期,就删除它们。
- 如果本次检查中,被删除的键超过了总数的 25%(即超过 5 个),那么就立即重复执行一轮这个过程,直到被删除的键的比例降下来,或者任务执行时间超过了某个阈值(为了避免阻塞主线程)。
优点:
- 主动清理内存:通过周期性地随机检查和删除,可以有效地清理掉那些从未被访问的过期键,缓解了内存浪费的问题。
缺点:
- 无法做到实时删除:由于是抽样检查,总会有“漏网之鱼”。一个键过期后,可能要等好几个周期才会被抽中并删除。
- CPU 资源消耗:虽然算法设计得非常精巧,尽量减少对主线程的影响,但它仍然需要占用一定的 CPU 时间。
3. 为什么是这两种策略的结合?
惰性删除和定期删除两者结合,形成了一个非常高效和稳健的过期键管理机制:
- 惰性删除确保了任何客户端在任何时候都绝对不会读取到已经过期的数据,保证了数据的正确性。
- 定期删除则作为一种兜底机制,持续地、主动地回收那些不再被访问的过期键所占用的内存,控制内存的整体使用情况。
这种组合策略,是在 CPU 性能、内存占用和数据实时性之间做出的一个非常经典的工程权衡。
4. RDB/AOF 和复制对过期策略的影响
持久化
- RDB:在执行
SAVE或BGSAVE创建 RDB 快照时,程序会忽略所有已经过期的键,不会将它们写入 RDB 文件。 - AOF:当一个键通过上述策略被删除时,Redis 会向 AOF 文件中追加一条
DEL命令,以确保在从 AOF 恢复时,这个过期键也被正确删除。在 AOF 重写时,同样会忽略已过期的键。
- RDB:在执行
主从复制
- 为了保证主从节点的数据一致性,从节点不会自己主动删除过期键。
- 当主节点(Master)上的某个键过期并被删除时(无论是惰性删除还是定期删除),主节点会向所有从节点(Slave)发送一个
DEL命令。 - 从节点接收到
DEL命令后,才会删除自己本地的对应键。
总结
| 策略 | 触发时机 | 优点 | 缺点 |
|---|---|---|---|
| 惰性删除 | 访问键时 | CPU 友好,保证数据绝不过期 | 内存不友好,可能浪费内存 |
| 定期删除 | 定时任务,随机抽查 | 内存友好,主动清理 | CPU 有少量开销,非实时 |
| 组合策略 | 两者结合 | CPU与内存平衡,数据正确,系统稳定 | - |
Redis 通过这种精巧的组合,实现了高效、可靠的键过期功能,这也是它作为高性能缓存和数据存储服务的关键特性之一。