跳到主要内容

内存管理与淘汰策略

问题

Redis 的过期删除策略是什么?有哪些内存淘汰策略?如何选择合适的淘汰策略?

答案

过期删除策略

Redis 使用惰性删除 + 定期删除两种策略配合删除过期 key。

策略触发时机说明
惰性删除访问 key 时检查读写 key 时发现过期才删除,CPU 友好但可能残留大量过期 key
定期删除每 100ms 周期执行随机抽取一批 key 检查过期,删除已过期的,平衡 CPU 和内存

定期删除的流程:

  1. 从设置了过期时间的 key 集合中随机取 20 个
  2. 删除其中已过期的 key
  3. 如果过期比例超过 25%,重复步骤 1
  4. 每轮执行时间不超过 25ms(防止阻塞主线程)
过期 key 可能残留

惰性 + 定期删除不保证所有过期 key 都被及时清理。如果大量 key 过期但一直没被访问,也没被定期抽到,就会一直占用内存。这时需要依靠内存淘汰策略兜底。

内存淘汰策略

当 Redis 使用内存达到 maxmemory 上限时,触发内存淘汰策略。

配置最大内存和淘汰策略
maxmemory 4gb
maxmemory-policy allkeys-lru

Redis 提供 8 种淘汰策略:

策略淘汰范围算法说明
noeviction不淘汰-内存满时拒绝写入(默认
allkeys-lru所有 keyLRU淘汰最近最少使用
allkeys-lfu所有 keyLFU淘汰最不经常使用
allkeys-random所有 key随机随机淘汰
volatile-lru有过期时间的 keyLRU只淘汰设了 TTL 的 key
volatile-lfu有过期时间的 keyLFU只淘汰设了 TTL 的 key
volatile-random有过期时间的 key随机只淘汰设了 TTL 的 key
volatile-ttl有过期时间的 keyTTL淘汰 TTL 最短的 key

LRU vs LFU

算法全称淘汰依据适用场景
LRULeast Recently Used最近最久没被访问热点数据稳定(适合大多数场景)
LFULeast Frequently Used访问次数最少存在短期热点(偶尔访问不应挤掉高频数据)
Redis 的近似 LRU

Redis 不使用标准 LRU(需要维护链表,开销大),而是近似 LRU:随机抽取 maxmemory-samples(默认 5)个 key,淘汰其中最久没访问的。样本数越大越接近真实 LRU,但 CPU 开销越大。

策略选择建议

场景推荐策略
纯缓存,所有 key 可淘汰allkeys-lruallkeys-lfu
缓存 + 持久数据混合volatile-lru(只淘汰有 TTL 的)
有明显冷热数据allkeys-lfu(Redis 4.0+)
不允许淘汰noeviction(内存满时写入报错)

内存优化技巧

技巧说明
合理设置过期时间避免大量 key 永不过期
使用 Hash 代替多个 String小 Hash 用 listpack 编码更省内存
控制 key 和 value 大小避免大 key
使用整数 keyint 编码最省内存
开启内存碎片整理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。

相关链接