跳到主要内容

内存管理与淘汰策略

问题

Redis 的过期删除策略是什么?内存满了怎么办?LRU 和 LFU 有什么区别?

答案

一、过期删除策略

Redis 使用 惰性删除 + 定期删除 的组合策略:

策略时机说明
惰性删除访问 key 时检查如果过期了才删除。节省 CPU,但可能残留大量过期 key
定期删除每秒执行多次随机抽取一批 key 检查过期。平衡 CPU 和内存
定期删除的细节

每秒运行 10 次(默认 hz=10),每次:

  1. 随机抽取 20 个设了过期时间的 key
  2. 删除其中过期的 key
  3. 如果过期比例 > 25%,重复步骤 1
  4. 每轮最多执行 25ms

二、内存淘汰策略

当内存使用达到 maxmemory 限制时触发:

策略说明推荐场景
noeviction不淘汰,写入报错不允许丢数据
allkeys-lru从所有 key 中淘汰最近最少使用的通用缓存(推荐)
allkeys-lfu从所有 key 中淘汰最不经常使用的有热点数据
allkeys-random从所有 key 中随机淘汰无特殊需求
volatile-lru从设了过期时间的 key 中 LRU 淘汰混合使用(缓存+持久)
volatile-lfu从设了过期时间的 key 中 LFU 淘汰混合使用
volatile-random从设了过期时间的 key 中随机淘汰
volatile-ttl淘汰剩余 TTL 最短的 key时效性数据

三、LRU vs LFU

对比项LRU(Least Recently Used)LFU(Least Frequently Used)
依据最近 访问时间访问频率
淘汰目标最久没被访问的访问次数最少的
适合场景一般缓存有热点数据
Redis 版本4.0 之前(近似 LRU)4.0 引入
LRU 的缺陷

一次性批量扫描的数据(如 KEYS * 或全表查询结果)会把真正的热点数据挤出去。LFU 通过统计访问频率避免了这个问题。

Redis 的近似 LRU

Redis 并没有维护精确的 LRU 链表(太耗内存),而是使用 近似 LRU

  • 每个 key 记录最后一次访问的时间戳(24 位,存在 Redis Object 头部)
  • 淘汰时随机采样 N 个 key(maxmemory-samples,默认 5)
  • 从中淘汰时间戳最旧的

maxmemory-samples 越大,越接近精确 LRU,但 CPU 开销也越大。

四、内存优化实践

优化手段说明
合理选择数据结构Hash 比多个 String 更省内存
控制 key 大小key 名精简,value 不要太大
设置合理过期时间避免内存中堆积无用数据
使用压缩业务层 Gzip/Snappy 压缩 value
控制连接数每个连接约占 10KB 输出缓冲
监控内存INFO memoryMEMORY USAGE key
# 查看内存使用
INFO memory
# used_memory: 已使用内存
# used_memory_rss: 操作系统分配的内存
# mem_fragmentation_ratio: 碎片率(> 1.5 需关注)

# 查看单个 key 的内存
MEMORY USAGE mykey

常见面试问题

Q1: maxmemory 应该设为多少?

答案
推荐设置为物理内存的 70%~80%。留 20%~30% 给:

  • 操作系统
  • fork 子进程(RDB/AOF 重写时的 Copy-On-Write)
  • 客户端输出缓冲区
  • 碎片开销

Q2: 缓存场景推荐用哪种淘汰策略?

答案

  • 纯缓存(所有数据都可丢):allkeys-lruallkeys-lfu
  • 缓存 + 持久数据混合volatile-lru(只淘汰设了过期时间的缓存 key,不动持久数据)
  • 有热点数据:优先选 LFU(Redis 4.0+)

Q3: 内存碎片怎么处理?

答案
mem_fragmentation_ratio 大于 1.5 时需要关注:

  • Redis 4.0+:开启自动碎片整理 activedefrag yes
  • 重启 Redis(重新加载数据后碎片消除)
  • 碎片的主要原因是频繁修改不同大小的 value

相关链接