内存管理与淘汰策略
问题
Redis 的过期删除策略是什么?有哪些内存淘汰策略?如何选择合适的淘汰策略?
答案
过期删除策略
Redis 使用惰性删除 + 定期删除两种策略配合删除过期 key。
| 策略 | 触发时机 | 说明 |
|---|---|---|
| 惰性删除 | 访问 key 时检查 | 读写 key 时发现过期才删除,CPU 友好但可能残留大量过期 key |
| 定期删除 | 每 100ms 周期执行 | 随机抽取一批 key 检查过期,删除已过期的,平衡 CPU 和内存 |
定期删除的流程:
- 从设置了过期时间的 key 集合中随机取 20 个
- 删除其中已过期的 key
- 如果过期比例超过 25%,重复步骤 1
- 每轮执行时间不超过 25ms(防止阻塞主线程)
过期 key 可能残留
惰性 + 定期删除不保证所有过期 key 都被及时清理。如果大量 key 过期但一直没被访问,也没被定期抽到,就会一直占用内存。这时需要依靠内存淘汰策略兜底。
内存淘汰策略
当 Redis 使用内存达到 maxmemory 上限时,触发内存淘汰策略。
配置最大内存和淘汰策略
maxmemory 4gb
maxmemory-policy allkeys-lru
Redis 提供 8 种淘汰策略:
| 策略 | 淘汰范围 | 算法 | 说明 |
|---|---|---|---|
noeviction | 不淘汰 | - | 内存满时拒绝写入(默认) |
allkeys-lru | 所有 key | LRU | 淘汰最近最少使用 |
allkeys-lfu | 所有 key | LFU | 淘汰最不经常使用 |
allkeys-random | 所有 key | 随机 | 随机淘汰 |
volatile-lru | 有过期时间的 key | LRU | 只淘汰设了 TTL 的 key |
volatile-lfu | 有过期时间的 key | LFU | 只淘汰设了 TTL 的 key |
volatile-random | 有过期时间的 key | 随机 | 只淘汰设了 TTL 的 key |
volatile-ttl | 有过期时间的 key | TTL | 淘汰 TTL 最短的 key |
LRU vs LFU
| 算法 | 全称 | 淘汰依据 | 适用场景 |
|---|---|---|---|
| LRU | Least Recently Used | 最近最久没被访问的 | 热点数据稳定(适合大多数场景) |
| LFU | Least Frequently Used | 访问次数最少的 | 存在短期热点(偶尔访问不应挤掉高频数据) |
Redis 的近似 LRU
Redis 不使用标准 LRU(需要维护链表,开销大),而是近似 LRU:随机抽取 maxmemory-samples(默认 5)个 key,淘汰其中最久没访问的。样本数越大越接近真实 LRU,但 CPU 开销越大。
策略选择建议
| 场景 | 推荐策略 |
|---|---|
| 纯缓存,所有 key 可淘汰 | allkeys-lru 或 allkeys-lfu |
| 缓存 + 持久数据混合 | volatile-lru(只淘汰有 TTL 的) |
| 有明显冷热数据 | allkeys-lfu(Redis 4.0+) |
| 不允许淘汰 | noeviction(内存满时写入报错) |
内存优化技巧
| 技巧 | 说明 |
|---|---|
| 合理设置过期时间 | 避免大量 key 永不过期 |
| 使用 Hash 代替多个 String | 小 Hash 用 listpack 编码更省内存 |
| 控制 key 和 value 大小 | 避免大 key |
| 使用整数 key | int 编码最省内存 |
| 开启内存碎片整理 | activedefrag yes(Redis 4.0+) |
查看内存使用
INFO memory
# used_memory: 已使用内存
# used_memory_rss: 操作系统分配的内存(含碎片)
# mem_fragmentation_ratio: 碎片率 = rss / used
# 碎片率 > 1.5 建议开启碎片整理
常见面试问题
Q1: Redis 的过期删除策略是什么?
答案:
Redis 使用惰性删除 + 定期删除两种策略:
- 惰性删除:访问 key 时才检查是否过期,过期则删除。优点是 CPU 友好,缺点是不被访问的过期 key 会一直残留
- 定期删除:每 100ms 从过期字典中随机取一批 key 检查,删除已过期的。如果过期比例高于 25% 则继续,每轮不超过 25ms
两者互补:惰性删除保证访问时的准确性,定期删除负责清理不被访问的过期 key。仍有残留时靠内存淘汰策略兜底。
Q2: 内存淘汰策略有哪些?怎么选?
答案:
8 种策略,分两大类:
- allkeys-*:从所有 key 中淘汰(适合纯缓存场景)
- volatile-*:只从有过期时间的 key 中淘汰(适合缓存和持久数据混合)
最常用的是 allkeys-lru(大多数缓存场景)和 allkeys-lfu(有冷热数据区分时)。
默认是 noeviction(不淘汰,满了报错),生产环境务必修改,否则内存满后所有写操作都会失败。
Q3: LRU 和 LFU 的区别?
答案:
- LRU:淘汰最近最久没访问的 key。问题:如果一个很少使用的 key 偶然被访问一次,就不会被淘汰,可能挤掉真正的热点数据
- LFU:淘汰访问频次最低的 key。更准确地反映数据的"热度",但需要额外维护访问计数器
Redis 的 LFU 实现使用了一个 8 位的计数器(lfu-log-factor),采用概率递增策略,既节省内存又能区分冷热数据。推荐有明显冷热差异的场景使用 LFU。