Redis 作为最流行的内存数据库之一,其高性能的核心在于数据全部存储在内存中。但内存资源有限,如何管理过期数据和内存不足时的数据淘汰,就成了 Redis 必须解决的关键问题。这也是面试中高频出现的考点,下面我们系统梳理一下。
一、过期键的删除策略
当我们使用 EXPIRE、PEXPIRE、EXPIREAT 等命令给键设置过期时间后,Redis 并不会立即删除这些键,而是采用了一套组合策略来平衡 CPU 和内存的消耗。
1. 定时删除(立即删除)
思路:在设置键过期时间的同时,创建一个定时器,到时间就立即删除该键。
优点:内存释放最及时,过期键不会占用内存。
缺点:如果过期键很多,删除操作会占用大量 CPU 时间,影响 Redis 的响应速度和吞吐量。定时器本身也需要消耗系统资源。
结论:Redis 没有采用这种策略。
2. 惰性删除(被动删除)
思路:键过期后不主动删除,只有当客户端访问该键时,才检查是否过期,若过期则删除并返回空。
优点:对 CPU 时间友好,删除操作只在访问时发生,不会浪费 CPU 去检查无关的键。
缺点:对内存不友好。如果一个键已经过期但一直不被访问,它会一直占用内存,造成内存泄漏。
实现:Redis 在 db.c 中的 expireIfNeeded 函数实现惰性删除,所有读写命令在执行前都会调用它。
3. 定期删除(主动删除)
思路:每隔一段时间,程序就对数据库进行一次检查,随机删除一部分过期键。这是定时删除和惰性删除的折中方案。
优点:通过限制删除操作的执行时长和频率,减少对 CPU 的影响,同时也能及时释放部分内存。
缺点:难以确定删除操作的执行时长和频率。太频繁会消耗 CPU,太少又会导致内存浪费。
Redis 的实现:Redis 默认每秒执行 10 次(hz 配置,默认 10)定期删除任务。每次执行时:
- 从所有设置了过期时间的数据库中随机抽取 20 个键;
- 删除其中已过期的键;
- 如果本次删除的过期键比例超过 25%,则重复上述步骤;
- 同时有一个时间上限(默认 25ms),避免阻塞主线程。
4. Redis 的最终策略
Redis 实际采用的是 惰性删除 + 定期删除 的组合策略。这样既避免了定时删除的 CPU 压力,又通过定期删除弥补了惰性删除可能导致的内存泄漏问题。
二、内存淘汰机制
即使有了过期键删除策略,当 Redis 内存使用达到 maxmemory 限制时,仍然需要一套内存淘汰机制来决定删除哪些数据。这是面试中另一个重点。
1. 配置参数
在 redis.conf 中通过 maxmemory 设置最大内存,通过 maxmemory-policy 设置淘汰策略。例如:
maxmemory 2gb
maxmemory-policy allkeys-lru
2. 八种淘汰策略
Redis 提供了 8 种内存淘汰策略,可分为四大类:
(1)不淘汰
noeviction:默认策略。当内存不足时,不淘汰任何数据,直接对写命令返回错误(读命令仍可执行)。这保证了数据不丢失,但会导致写服务不可用。
(2)基于过期时间的淘汰
volatile-lru:从设置了过期时间的键中,使用 LRU 算法淘汰最久未使用的键。volatile-lfu:从设置了过期时间的键中,使用 LFU 算法淘汰使用频率最低的键(Redis 4.0 引入)。volatile-random:从设置了过期时间的键中,随机淘汰。volatile-ttl:从设置了过期时间的键中,优先淘汰剩余生存时间(TTL)最短的键。
(3)基于所有键的淘汰
allkeys-lru:从所有键中,使用 LRU 算法淘汰最久未使用的键。allkeys-lfu:从所有键中,使用 LFU 算法淘汰使用频率最低的键(Redis 4.0 引入)。allkeys-random:从所有键中,随机淘汰。
3. LRU 与 LFU 的区别
- LRU(Least Recently Used):淘汰最近最久未使用的键。它关注的是“最近一次访问时间”,适合有明显热点数据的场景。
- LFU(Least Frequently Used):淘汰访问频率最低的键。它关注的是“访问次数”,适合热点数据相对稳定的场景。LFU 能避免偶发的一次访问就把冷数据“救活”的问题。
4. Redis 的近似 LRU 算法
标准的 LRU 需要维护一个双向链表,内存开销大。Redis 采用了近似 LRU 算法:
- 每个对象有一个 24 位的
lru字段,记录最后一次访问的时间戳。 - 淘汰时,随机抽取
maxmemory-samples(默认 5)个键,从中选择最久未使用的键淘汰。 - 抽取的样本数越大,越接近真实 LRU,但 CPU 消耗也越高。
这种设计用很小的内存代价换取了接近 LRU 的效果,是 Redis 在性能和内存之间的经典权衡。
5. 如何选择淘汰策略
- 如果数据有明显冷热区分,推荐
allkeys-lru。 - 如果数据访问频率差异大且热点稳定,推荐
allkeys-lfu。 - 如果希望只淘汰过期数据,保留永久数据,推荐
volatile-lru或volatile-lfu。 - 如果数据都不能丢失,使用
noeviction并做好内存监控和告警。
三、总结
| 维度 | 过期键删除策略 | 内存淘汰机制 |
|---|---|---|
| 触发条件 | 键设置了过期时间且已到期 | 内存使用达到 maxmemory |
| 核心策略 | 惰性删除 + 定期删除 | 8 种策略,常用 allkeys-lru |
| 关注点 | CPU 与内存的平衡 | 内存不足时保留哪些数据 |
理解这两套机制,不仅能帮助你在面试中从容作答,更能在实际生产环境中合理配置 Redis,避免内存溢出或性能抖动。建议结合 INFO memory、INFO stats 等命令监控 evicted_keys、expired_keys 等指标,做到心中有数。
未经允许不得转载:任鹏个人博客 » Redis 过期键的删除策略和内存淘汰机制

