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。
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 需要更长的学习期,新写入的键初始频率低,容易被淘汰。
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 行为。 - 监控先行: 淘汰事件是内存压力的直接信号,必须纳入生产监控体系。