4. Redis 过期策略与内存淘汰:过期键删除策略、LRU/LFU/TTL 淘汰算法、maxmemory 配置

Redis 作为内存数据库,其存储容量受物理内存限制。当数据量增长或写入速度超过清理速度时,必须有一套严谨的机制来管理键的生命周期和内存水位。本章深入剖析 Redis 的两层内存管理机制:过期键删除策略(决定“何时删除已过期的键”)和内存淘汰策略(决定“内存满时删除哪些键”),并详解核心配置 maxmemory。

4.1 过期键删除策略:三种方案的权衡

Redis 对设置了 TTL(Time To Live)的键,不会在到期瞬间立即删除,而是通过以下三种策略的组合来平衡 CPU 开销与内存占用。

策略名称 执行时机 优点 缺点 Redis 默认行为
定时删除 键过期时立即触发删除 内存释放及时,几乎无过期键残留 CPU 负担重,大量键同时过期会导致服务卡顿 ❌ 未采用
惰性删除 每次访问键时检查是否过期 CPU 开销极低,只处理被访问的键 过期键可能长期占用内存,形成“内存泄漏” ✅ 核心策略之一
定期删除 每隔一段时间(默认 100ms)随机抽样检查 平衡 CPU 与内存,避免大量过期键堆积 参数调优复杂,抽样频率需根据业务调整 ✅ 核心策略之一

实际运行机制: Redis 采用 惰性删除 + 定期删除 的组合方案。惰性删除保证每次访问时清理当前键;定期删除作为后台守护,每 100ms 随机抽取 20 个带 TTL 的键,删除其中已过期的键,若过期比例超过 25% 则重复抽样,最多持续 25ms。

Redis 过期键删除策略组合流程 Redis 过期键删除策略组合流程 惰性删除 访问键时检查过期 定期删除 每 100ms 抽样 20 个键 定时删除 到期立即删除(未采用) 组合策略执行结果 1. 客户端访问 key → 惰性删除检查,过期则立即删除 2. 后台定时任务 → 随机抽样,过期比例 > 25% 则重复 3. 两种策略互补:惰性删除保实时,定期删除防堆积

4.2 内存淘汰策略:当 maxmemory 被触及

当 Redis 已用内存达到 maxmemory 配置上限时,写入操作会触发内存淘汰策略。Redis 8 种淘汰策略可分为三大类:不淘汰、LRU 近似算法、LFU 近似算法 和 TTL 优先。

4.2.1 maxmemory 配置

# redis.conf 配置示例
maxmemory 4gb                     # 最大内存限制,单位:bytes/kb/mb/gb
maxmemory-policy allkeys-lru      # 淘汰策略,默认 noeviction
maxmemory-samples 5               # LRU/LFU/TTL 抽样数量,默认 5,可调至 10

生产建议: maxmemory 建议设置为物理内存的 60%-70%,预留空间给操作系统、持久化子进程(如 RDB 快照、AOF 重写)以及突发流量。设置过满会导致系统 OOM(Out Of Memory)或被内核杀死。

4.2.2 八种淘汰策略详解

策略名称 作用范围 淘汰依据 适用场景
noeviction 全部键 不淘汰,写入返回 OOM 错误 缓存不可丢失的强一致性场景
allkeys-lru 全部键 最近最少使用(LRU 近似) 通用缓存,访问模式有明显热点
allkeys-lfu 全部键 最不经常使用(LFU 近似) 访问频率差异大的场景
allkeys-random 全部键 随机淘汰 访问模式均匀,无热点
volatile-lru 仅带 TTL 的键 最近最少使用(LRU 近似) 混合存储:永久键保留,过期键淘汰
volatile-lfu 仅带 TTL 的键 最不经常使用(LFU 近似) 带 TTL 的键有频率差异
volatile-random 仅带 TTL 的键 随机淘汰 带 TTL 的键访问均匀
volatile-ttl 仅带 TTL 的键 剩余 TTL 最短优先 优先淘汰即将过期的键

关键陷阱: 当使用 volatile-* 系列策略时,若所有带 TTL 的键都被淘汰完,但内存仍然不足,Redis 会回退到 noeviction 行为——直接返回 OOM 错误,而不是去淘汰永久键。务必确保带 TTL 的键数量足够,或直接使用 allkeys-* 策略。

4.3 LRU 与 LFU 近似算法:Redis 的实现细节

Redis 并未实现严格的 LRU(Least Recently Used)或 LFU(Least Frequently Used),而是采用近似算法来平衡性能与精度。

4.3.1 近似 LRU 算法

  • 核心思想: 从 maxmemory-samples(默认 5)个键中随机抽样,淘汰其中空闲时间最长的键。
  • 数据结构: 每个 Redis 对象头(robj)中有一个 24 位的 lru 字段,存储最近一次访问的 Unix 时间戳(秒级,精度有限)。
  • 与严格 LRU 的差距: 抽样数越大,淘汰精度越接近严格 LRU。官方测试显示,maxmemory-samples 10 时近似 LRU 已非常接近理论 LRU。
# 调整抽样数量提升 LRU 精度
maxmemory-samples 10

4.3.2 近似 LFU 算法

Redis 4.0 引入 LFU 策略,使用 Morris Counter(概率计数器)来近似统计访问频率,而非精确计数。

参数 配置项 默认值 说明
计数器衰减因子 lfu-decay-time 1 每 N 分钟计数器减半,控制热度衰减速度
计数器对数因子 lfu-log-factor 10 控制计数器增长曲线,越大则高频键增长越慢

LFU 优势: 相比 LRU,LFU 能更好抵抗“周期性扫描”导致的缓存污染。例如,一个低频但重要的键不会被一次批量查询挤出缓存。但 LFU 需要更长的学习期,新写入的键初始频率低,容易被淘汰。

LRU 与 LFU 淘汰决策对比 LRU 与 LFU 淘汰决策对比 近似 LRU 淘汰逻辑 1. 随机抽样 N 个键(默认 5) 2. 比较每个键的 lru 时间戳 3. 淘汰空闲时间最长的键 特点:关注“最近是否被访问” 缺陷:周期性扫描会污染缓存 优化:增大 maxmemory-samples 适用:有明显时间局部性的访问模式 近似 LFU 淘汰逻辑 1. 随机抽样 N 个键(默认 5) 2. 比较 Morris Counter 计数值 3. 淘汰访问频率最低的键 特点:关注“历史访问频率” 缺陷:新键初始频率低,易被误淘汰 优化:调整 lfu-log-factor / lfu-decay-time 适用:有明显频率局部性的访问模式 vs

4.4 实战配置与监控

4.4.1 配置推荐矩阵

业务场景 推荐策略 maxmemory-samples 说明
通用缓存(热点明显) allkeys-lru 10 平衡精度与性能,最常用
内容推荐/计数缓存 allkeys-lfu 10 高频内容优先保留
Session 存储(带 TTL) volatile-ttl 5 优先淘汰即将过期的 Session
数据不可丢失 noeviction — 配合监控告警,内存满时拒绝写入
读写均匀无热点 allkeys-random — 避免 LRU/LFU 的额外计算开销

4.4.2 监控淘汰行为

# 查看淘汰统计信息
redis-cli INFO stats | grep evicted_keys
evicted_keys:12345          # 累计淘汰的键数量

# 查看内存使用与 maxmemory
redis-cli INFO memory | grep -E "used_memory|maxmemory"
used_memory:4294967296
maxmemory:8589934592

# 实时监控淘汰事件(Redis 7.0+)
redis-cli MONITOR | grep "evict"

告警阈值: 当 evicted_keys 在短时间内快速增长,说明内存压力大,应考虑扩容或优化数据有效期。建议设置告警:连续 5 分钟淘汰速率 > 100 keys/s。

4.5 本章小结

  • 过期删除: 惰性删除 + 定期删除组合,避免 CPU 峰值与内存泄漏。
  • 内存淘汰: 8 种策略覆盖不同访问模式,allkeys-lru 是通用首选。
  • 近似算法: LRU 关注时间局部性,LFU 关注频率局部性,通过抽样和 Morris Counter 实现高效近似。
  • 配置调优: maxmemory 预留余量,maxmemory-samples 控制精度,lfu-decay-time 和 lfu-log-factor 控制 LFU 行为。
  • 监控先行: 淘汰事件是内存压力的直接信号,必须纳入生产监控体系。