volatile-lru与allkeys-lru的核心区别在于淘汰范围:前者仅在设置了过期时间的key中按最近最少使用淘汰,后者在所有key(含永久key)中按相同规则淘汰;误配volatile-lru而存入大量无ttl数据会导致oom写拒绝。

直接说结论:LRU适合有明显时间局部性、访问模式偏“短期热点”的场景;LFU更适合识别长期稳定高频访问的数据,但对突发流量或周期性访问不敏感。选错策略,缓存命中率可能掉20%以上,而且问题往往在大促或流量突增时才暴露。
volatile-lru 和 allkeys-lru 的核心区别在哪?
关键不是“要不要淘汰”,而是“淘汰谁的范围”:
-
volatile-lru只在设置了TTL的 key 里找最久未访问的——适合会话、临时令牌、验证码这类天然带过期时间的数据; -
allkeys-lru在所有 key(含永久 key)中找最久未访问的——适合缓存层统一管理、不依赖 TTL 控制生命周期的场景(比如预热后长期有效的配置); - 如果误配
volatile-lru却往 Redis 里塞了大量无 TTL 的业务数据,内存打满时 Redis 会直接返回(error) OOM command not allowed when used memory > 'maxmemory'.,而不是淘汰; -
noeviction不是“不淘汰”,而是拒绝写入,它和volatile-lru混用时容易让人误以为“有 TTL 就安全”,其实没设 TTL 的 key 会卡住整个写流程。
为什么 allkeys-lfu 不等于“更智能”,反而容易出问题?
LFU 在 Redis 里复用了 lru 字段的 24 位空间,拆成两段:16 位存“最后衰减时间”,8 位存“访问频次计数器(LOG_C)”。这意味着:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 频次不是线性增长:计数器用的是对数增长(
LOG_C),避免小 key 频次爆炸,但也会导致“访问 10 次”和“访问 100 次”在计数器上可能只差 1; - 衰减机制默认每分钟触发一次,旧访问记录会逐步“贬值”——但如果业务有固定周期行为(比如每天凌晨拉一次报表),LFU 可能还没等到下一次访问,频次就衰减归零,结果被当成冷数据淘汰;
-
maxmemory-samples对 LFU 影响更大:采样数太小(如默认 5),随机抽到的 key 频次分布偏差大,淘汰结果波动剧烈;建议压测时调到 10~20,但别超过 30,否则 CPU 开销明显上升; - LFU 不处理“单次扫描污染”:全表导出类操作会批量 touch 大量 key,它们的频次瞬间抬高,挤占真正热点的 slot,这点比 LRU 更难规避。
怎么验证你当前的策略是否真起作用?
不能只看 INFO memory 里的 mem_allocator 或 used_memory_human,得盯三个指标:
- 查淘汰动作:
INFO stats中的evicted_keys是否持续增长(说明真在淘汰),同时对比expired_keys(过期驱逐)——如果后者远高于前者,说明volatile-*策略其实在靠 TTL 自清理,淘汰策略形同虚设; - 看采样质量:
CONFIG GET maxmemory-samples和实际 key 总量的比例——若 key 总数 100 万,采样仅 5,那淘汰基本靠运气; - 抓真实行为:用
redis-cli --scan --pattern "*" | xargs -L 1 -I {} redis-cli object freq {} 2>/dev/null批量查 LFU 频次(注意:该命令在大实例上慎用,会阻塞);对 LRU 则可用object idletime查空闲秒数,观察分布是否符合预期(比如 90% 的 key idle
最常被忽略的一点:Redis 的 LRU/LFU 都是“近似”算法,没有绝对正确答案。真正决定效果的,不是选 allkeys-lfu 还是 volatile-lru,而是你有没有结合 maxmemory-samples、lfu-log-factor、lfu-decay-time 这几个参数做针对性调优,以及——是否把不该进缓存的数据(比如日志、调试信息)也塞了进去。










