Redis LRU和LFU均为近似算法:LRU基于24位分钟级时间戳采样淘汰最久未访问key;LFU复用该字段,高16位存对数频次、低8位存衰减时间戳,通过频次衰减机制识别近期热点。

Redis 内存写满触发淘汰时,LRU 和 LFU 不是靠“精确排序”计算,而是基于采样 + 字段复用的近似算法,核心区别在判断依据和字段含义。
LRU:靠“最后访问时间戳”做近似比较
Redis 并不维护全局访问链表。每个 key 的 redisObject 结构里只存一个 24-bit 的 lru 字段,记录的是分钟级精度的时间戳(不是毫秒,也不是纳秒),表示该 key 上次被访问距 Redis 启动后的大致分钟数。
- 淘汰时,Redis 随机选取 maxmemory-samples 个 key(默认 5 个);
- 从这组样本中,挑出 lru 字段值最小的那个 key(即“最久没被访问”的那个);
- 这个值越小,代表它最后一次访问距离现在越久——注意,因精度粗、采样随机,它只是近似 LRU,不是严格按时间排序。
LFU:靠“频次计数器 + 衰减时间”动态评估热度
LFU 复用同一个 24-bit lru 字段:高 16 位存对数频次(logc),低 8 位存上次衰减发生的时间戳(分钟级)。
- 每次读操作(如 GET、HGET)触发频次增长,但不是 +1,而是按公式 logc = logc + 1 / (logc + 1) 增长,防止高频 key 计数器过快溢出;
- 每过约 1 分钟(由低 8 位时间戳推算),Redis 会扫描所有 key,对频次做右移衰减(相当于乘以衰减系数),让旧访问权重自然下降;
- 淘汰时同样随机采样,选出 频次最低(高 16 位最小)的 key;若频次相同,则比对低 8 位时间戳,选更早衰减的(即更“陈旧”的低频)。
关键差异不在“怎么算”,而在“算什么”
LRU 关注的是单次访问距今有多远,适合短期局部性明显的场景(比如用户 session);LFU 关注的是近期被访问的密集程度,且自带衰减机制,更适合识别稳定热点(比如首页 banner、通用配置)。但 LFU 对突发扫描类请求更敏感——一次批量读可能把几千个 key 频次拉高,又因衰减慢,短期难以回落。
真正影响结果的,常是 volatile- 还是 allkeys- 前缀
算法再精细,也得看作用范围:
- 选 volatile-lru:只在带 TTL 的 key 中采样淘汰,永不过期的配置、字典类数据完全不受影响;
- 选 allkeys-lfu:所有 key 都参与采样,哪怕你给每个 key 都设了 30 分钟 TTL,它也会把刚写入的冷 key 和高频配置一起比——语义上不如 volatile-lfu 清晰;
- 如果业务里混用了带 TTL 和不带 TTL 的 key,却误配 allkeys-*,可能把关键静态数据直接踢出。


















