Redis 作为缓存运行时,内存是硬约束。maxmemory 设置上限后,写入超限时的行为由 maxmemory-policy 决定——这就是内存淘汰(eviction)。本章解析 8 种策略、近似 LRU 的实现(为什么不用精确 LRU)、LFU 的频率统计(Redis 4.0+)与生产选型。
performEvictions);maxmemory 0 表示不限制。| 策略 | 作用域 | 行为 |
|---|---|---|
noeviction(默认) | - | 写命令直接报 OOM 错误,不淘汰 |
allkeys-lru | 全部 key | 淘汰最久未使用 |
allkeys-lfu | 全部 key | 淘汰最不常使用 |
allkeys-random | 全部 key | 随机淘汰 |
volatile-lru | 有 TTL 的 key | 淘汰最久未使用 |
volatile-lfu | 有 TTL 的 key | 淘汰最不常使用 |
volatile-random | 有 TTL 的 key | 随机淘汰 |
volatile-ttl | 有 TTL 的 key | 淘汰剩余 TTL 最短 |
注意:Redis 8.6 起新增
allkeys-lrm/volatile-lrm(Least Recently Modified,按"最久未修改"淘汰;读取不会刷新其时钟,可防全量扫描污染缓存,8.6.2 实测可用)。8.0–8.4 及更早版本为上述 8 种。
选型速记:
allkeys-lru(帕累托法则:少部分 key 承载大部分访问);allkeys-lfu(防"一次性热点"霸占 LRU);noeviction 适合把 Redis 当数据库用的场景(宁可报错不可丢数据)。精确 LRU 需要维护双向链表记录全局访问序,代价:
Redis 采用近似 LRU:每个 key 保存一个 24 bit 的 LRU clock(lru 字段,记录"最近一次访问"的相对时间戳),淘汰时随机采样 5 个 key(maxmemory-samples,可用 CONFIG SET 调整),驱逐其中 LRU clock 最旧的。
误差分析:maxmemory-samples 越大,近似越接近精确 LRU,但淘汰计算成本越高。官方文档给出的测试曲线显示:默认采样 5 已能较好逼近精确 LRU,采样 10 更接近——具体差距因数据分布而异,建议结合自身访问模式压测。
LRU 的缺陷:冷门 key 被一次性访问后长期霸占(如双十一大促的爆款商品,活动结束后不再访问,却因"最近用过"而存活)。LFU(Least Frequently Used)按访问频率淘汰。
LFU 复用 lru 字段的 24 bit,拆分为两部分:
lru 字段(24 bit)
├── 高 16 bit:LDT(Last Decrement Time,上次衰减时间,分钟级)
└── 低 8 bit:counter(访问频率计数,0-255)lfu-decay-time(分钟)线性折算——每经过该时长未访问,counter 减 1(按"距上次衰减的分钟数 / lfu-decay-time"计算,减到 0 为止)——保证"曾经热"≠"现在热";maxmemory-policy allkeys-lfu
lfu-log-factor 10 # counter 增长因子:越大热 key 增长越慢
lfu-decay-time 1 # 衰减周期(分钟):越大衰减越慢触发淘汰时,Redis 会循环驱逐直到内存降回 maxmemory 之下(每轮采样驱逐一批),INFO stats 的 evicted_keys 累计被驱逐数量——监控它:突增说明容量规划不足。
maxmemory-policy noeviction 下缓存写满直接报错,业务雪崩;OBJECT IDLETIME 只在 LRU 策略下有意义、OBJECT FREQ 只在 LFU 策略下有意义,且二者都不会改动访问时钟(不同于 TOUCH);maxmemory-samples 与监控提前预警。allkeys-lru 是默认最优解;maxmemory-samples 权衡精度与成本);evicted_keys 与 maxmemory 使用率,容量规划永远优于被动淘汰。下一章讲解懒惰删除(lazy free):UNLINK 与 BIO 异步回收的内存治理利器。