Redis 过期与淘汰策略

redis 📚 learning redis-expiration-eviction · redis · expiration · eviction · lru · lfu · ttl · memory-management

TL;DR(30 秒扫完)

  • 过期策略 = 惰性删除 + 定期删除(组合使用,不是独立线程)
  • 淘汰策略 = 8 种 maxmemory-policy(范围 × 算法的正交矩阵)
  • LRU 是近似的:采样 5 个候选选最老,不是严格 LRU
  • LFU 是 Redis 4.0+:15-bit 计数器 + 10 秒时间衰减,防热点长尾
  • 默认是 noeviction:内存满直接写失败,生产必须显式配置

关键结论

结论 A过期扫描是主线程 serverCron 定时任务,不是独立线程
结论 B8 种淘汰策略 = 2 种范围(allkeys/volatile)× 4 种算法(无/随机/LRU/LFU/TTL)
结论 CLRU/LFU 都是"近似"的——维护链表+Hash 开销大,采样更划算
结论 Dvolatile-* 策略如果可删 key 不够会返回错误,生产慎用

完整讲解(费曼四步)

STEP 1 · 概念

过期策略:如何处理到达 TTL 的 key(Redis 只有两种机制组合) 淘汰策略:内存达到 maxmemory 时如何腾空间(8 种策略) 两者独立但会同时发生(如 volatile-lru 既检查 TTL 又检查 LRU)。

STEP 2 · 大白话

过期策略比喻:
  • 惰性删除:快递过期了,有人来取才扔掉(访问时才检查)
  • 定期删除:物业每天巡楼扔过期快递(主线程定时扫,但只扫一部分)
淘汰策略比喻:
  • noeviction:柜子满了就不放新书(默认,写失败)
  • allkeys-lru:柜子满了就扔最久没翻的书(全部 key 中淘汰 LRU)
  • volatile-lru:柜子满了就扔有到期日且最久没翻的书(只在有 TTL 的 key 中淘汰)
  • allkeys-lfu:柜子满了就扔翻得最少的书(基于访问频率)

STEP 3 · 底层

过期策略两大机制(组合使用)

① 惰性删除(Lazy Expiration)
  • 访问 key 时(lookupKey 阶段)判断 expire < now
  • 过期就调 expireDictGenericDelete 立即删除
  • 优点:零开销(只有访问才触发)
  • 缺点:冷门 key 永不被访问 → 内存一直占着
② 定期删除(Periodic Expiration)
  • 主线程定时器 serverCron(每秒约 10 次)触发 activeExpireCycle
  • 每次从 expires 字典随机抽 20 个有过期时间的 key
  • 统计这 20 个中实际过期的比例:
  • 过期比例 ≤ 25%:本轮结束(说明整体过期不多)
  • 过期比例 > 25%:继续循环,直到 CPU 时间超过 25ms 或扫完
  • 优点:控制过期 key 数量,避免内存泄漏
  • 缺点:采样有遗漏,个别 key 会晚删
关键澄清:不是独立线程,是主事件循环里的定时任务。通过采样 + 时间上限保证不阻塞命令执行。 为什么不用主动全扫?
  • 单线程全扫会阻塞所有命令,延迟飙升
  • 采样 + 时间限制是 CPU 与内存的折中
实现细节:
  • 过期信息存 expires 字典(key → unixtime ms),与 db 主字典分离
  • EXPIRE/PEXPIRE/SET EX 写 expires 字典
  • 每次命令执行前后都可能触发一次 activeExpireCycle

内存淘汰策略 8 种(正交矩阵)

范围淘汰算法策略名说明
全部 key无noeviction默认,写失败返回错误
全部 key随机allkeys-random从所有 key 随机删
全部 keyLRUallkeys-lru从所有 key 淘汰最近最少使用
全部 keyLFUallkeys-lfu从所有 key 淘汰最不经常使用
有过期时间的 key随机volatile-random只在有 TTL 的 key 中随机删
有过期时间的 keyLRUvolatile-lru只在有 TTL 的 key 中淘汰 LRU
有过期时间的 keyLFUvolatile-lfu只在有 TTL 的 key 中淘汰 LFU
有过期时间的 keyTTLvolatile-ttl优先淘汰剩余 TTL 最小的
范围维度:
  • allkeys-*:无论有没有过期时间都参与淘汰(缓存场景推荐)
  • volatile-*:只在设置了 EXPIRE 的 key 中淘汰;如果这类 key 不够删,就返回错误

LRU 是「近似 LRU」——非严格 LRU

  • 严格 LRU 需要维护双向链表 + Hash,开销大
  • Redis 用采样淘汰:随机抽 maxmemory-samples(默认 5)个候选,选最老淘汰
  • 默认采样 5 个误差约 1%,可配置 10-100 提升精度但增加开销
  • 时间戳存在 key 的 lastAccessedTime 字段(4 字节)

LFU 是「近似 LFU」——Redis 4.0+ 新增

  • 15-bit 计数器存在 key 结构里,记录访问频率
  • 计数器采用时间衰减:每 10 秒(lfu-log-freq-factor 控制)衰减一次
  • 新 key 首次访问给初始值(默认 5)
  • 优势场景:热点分布长尾时,防止新数据挤走真正的热点

noeviction 的坑

  • 默认策略,内存满后所有写操作返回 OOM command not allowed when used memory exceeds 'maxmemory'
  • 生产环境必须显式配置淘汰策略,不能让默认值扛
  • 业务层要处理这个错误(重试/降级)

选型建议

场景推荐策略原因
通用缓存allkeys-lru有 TTL 无 TTL 一起淘汰,不挑
热点长尾allkeys-lfu防新数据挤走真正热点
需要保留数据volatile-lru只淘汰有 TTL 的,但可能返回错误
会话缓存volatile-ttl优先淘汰剩余 TTL 最小的
数据安全优先noeviction写失败好过丢数据,但业务要处理

监控指标

INFO stats:
  evicted_keys: 被淘汰的 key 数(内存驱动)
  expired_keys: 过期的 key 数(TTL 驱动)

CONFIG GET maxmemory-policy  # 查当前策略

STEP 4 · 简化

一句话总结:过期=惰性+定期(主线程定时扫),淘汰=8 种策略(范围×算法),LRU/LFU 都是近似的。 记忆口诀:
  • 过期:访问删 + 定时扫(20 个/25%/25ms)
  • 淘汰:8 种 = 2 范围(allkeys/volatile)× 4 算法(无/随机/LRU/LFU/TTL)
  • LRU:采样 5 个选最老
  • LFU:15-bit 计数器 + 10 秒衰减
  • 默认:noeviction(写失败)

常见误区

说"Redis 有专门的过期线程"
是主线程 serverCron 定时任务
说"过期 key 立即删除"
不保证,采样机制有概率延迟
说"LRU 是严格 LRU"
是采样的近似 LRU,维护链表开销大
说"LFU 从 3.0 就有"
Redis 4.0 新增
说"淘汰和过期是一回事"
过期是 TTL 驱动,淘汰是内存驱动,两个独立机制
说"默认策略是 allkeys-lru"
默认是 noeviction

延伸追问

activeExpireCycle 的具体逻辑?
采样 20 个过期 key,过期比例 >25% 继续,单次最多 25ms;serverCron 每秒 10 次 + 命令前后触发
如何判断过期 key 堆积?
INFO keyspace 看 expired_keys;MEMORY USAGE 看内存;DBSIZE 与真实存活数对比
volatile-lru 和 allkeys-lru 怎么选?
有 TTL 的 key 占比高用 volatile-lru;混合场景用 allkeys-lru
evicted_keys 突增如何排查?
INFO memory 看 used_memory vs maxmemory;INFO stats 看 evicted_keys 速率;确认是策略主动淘汰还是内存爆表
Redis 7.0 对过期清理有什么优化?
过期清理的 CPU 时间控制更精细;activeExpireCycle 支持按数据库独立触发
maxmemory-samples 怎么调优?
默认 5 误差约 1%;提高到 10-100 精度提升但开销增大;一般不改

速查表

过期: 惰性删除 + 定期删除(主线程 serverCron 定时扫)
定期扫: 20 个/次, 过期率 >25% 继续, 单次最多 25ms
淘汰: 8 种 = 2 范围(allkeys/volatile) × 4 算法(无/随机/LRU/LFU/TTL)
LRU: 近似, 采样 maxmemory-samples(默认 5) 个选最老
LFU: 近似, 15-bit 计数器 + 10 秒衰减, Redis 4.0+
默认: noeviction(写失败)
监控: INFO stats → evicted_keys / expired_keys

Anki 候选卡片

Q: Redis 过期策略的两个机制?
A: 惰性删除 + 定期删除(组合使用)
Q: 定期删除的采样参数?
A: 20 个/次,过期率 >25% 继续,单次最多 25ms
Q: Redis 内存淘汰策略有几种?
A: 8 种 = 2 范围(allkeys/volatile)× 4 算法(无/随机/LRU/LFU/TTL)
Q: LRU 是严格 LRU 吗?
A: 不是,是采样的近似 LRU,默认抽 5 个候选选最老
Q: LFU 从哪个版本开始支持?
A: Redis 4.0,15-bit 计数器 + 10 秒时间衰减
Q: Redis 默认淘汰策略?
A: noeviction(内存满写失败返回错误)

关联题目

  • 《Redis 的过期策略是怎么样的?》— 2026-09-20 Round 1 Q5, ⭐⭐(误说独立线程,遗漏采样细节)
  • 《Redis的内存淘汰策略是怎么样的?》— 2026-09-20 Round 1 Q6, ⭐⭐⭐⭐☆(框架正确,缺策略名称)

关联知识

过期是 TTL 驱动的删除,淘汰是内存驱动的选择性删除——两个独立机制
过期像快递过期自动销毁;淘汰像仓库满了只能扔掉最不重要的
✦ 记 忆 口 诀 ✦
过期=惰性+定期;淘汰=8 种策略(范围×算法矩阵)
关键可视化
过期 vs 淘汰两个独立机制
flowchart TB
  M["Redis 内存管理"] --> E["过期策略<br/>TTL 驱动"]
  E --> E1["惰性删除<br/>访问时才检查"]
  E --> E2["定期删除<br/>serverCron 抽样"]
  M --> V["淘汰策略<br/>maxmemory 触发"]
  V --> V1["2 种范围<br/>allkeys/volatile"]
  V --> V2["4 种算法<br/>无/随机/LRU/LFU/TTL"]
8 种淘汰策略矩阵
flowchart TB
  subgraph 范围
    R1["allkeys-*<br/>所有 key"]
    R2["volatile-*<br/>带 TTL 的 key"]
  end
  subgraph 算法
    A1["noeviction<br/>直接失败"]
    A2["random<br/>随机"]
    A3["lru<br/>最近最少用"]
    A4["lfu<br/>最不常用"]
    A5["ttl<br/>最短过期"]
  end
  R1 --> A1 & A2 & A3 & A4
  R2 --> A1 & A2 & A3 & A4 & A5
LFU 15-bit 计数器 + 10 秒时间衰减
flowchart LR
  C["LFU 计数器<br/>15-bit"] --> T["时间衰减"]
  T --> D["每小时 -1<br/>防热点长尾"]
  subgraph 特点
    P1["采样 5 个候选<br/>选最不常用"]
    P2["Redis 4.0+<br/>默认 lfu"]
    P3["比 LRU 更精确<br/>但开销稍大"]
  end
知识关系

⬆️ 前置(Prerequisite)

redis-single-thread

🔄 延伸(Extends)

暂无

⚡ 对比(Contrast)

缓存三失效— 雪崩根因之一是 TTL 集中过期,TTL 抖动是防雪崩的重要手段缓存一致性— TTL 是最终一致性的安全网
🎯 概念 📏 规则 ⚠️ 误区 🔍 追问 ✨ 口诀 共 0 张卡,点击翻面