redis的lru是故意采用近似算法而非严格实现,因严格lru需额外指针开销大、引发cache line失效且无法原子操作;maxmemory-samples控制采样数,默认5,影响淘汰精度与cpu开销;lfu下该参数仍生效但比较逻辑变为冷热权重;volatile策略下仅对带ttl的key采样,且lru字段不随expire重置。

Redis 的 LRU 不是“没做好”,而是故意不做严格版——因为严格 LRU 在 Redis 里既吃内存又拖性能,根本跑不起来。
为什么严格 LRU 在 Redis 中不可行
严格 LRU 要求每个 key 都维护在双向链表中的精确位置,每次 GET 或 SET 都得把对应节点挪到表头。这带来三个硬伤:
- 每个 key 得额外存两个指针(
prev/next),亿级 key 场景下,光指针就多占 16–24 字节/键,远超lru字段本身的 3 字节 - 链表重排触发 cache line 失效和内存跳转,实测在 10 万 QPS 下,这部分开销占淘汰逻辑总耗时超 12%
- 哈希表查找 + 链表移动无法原子合并,导致
OBJECT IDLETIME返回的时间可能滞后于真实访问顺序
maxmemory-samples 是怎么影响淘汰精度的
这个参数直接控制 Redis 每次淘汰时随机采样的 key 数量,默认是 5。它不改变算法本质,但决定了“近似”有多近:
- 设为
1:等效随机淘汰,完全丢失时间维度,误删高频 key 概率极高 - 设为
5:通用平衡点,适合访问均匀的场景(如 CDN 缓存静态资源) - 设为
10~15:对热点倾斜明显(如 session key 前缀集中)的业务,误淘汰率下降约 40%,CPU 开销几乎无感 - 设为
>20:采样循环本身在evict.c中成为瓶颈,收益递减甚至负向
LFU 模式下 maxmemory-samples 还管用吗
管用,但行为变了。LFU 复用了 lru 字段(高 16 位存衰减时间,低 8 位存频次对数),而 maxmemory-samples 决定的是采样范围——LFU 淘汰时仍要从这些样本中比对“冷热权重”,只是比较逻辑不再是纯时间戳大小。
容易被忽略的一点:OBJECT IDLETIME 在 LFU 模式下返回的不是“距上次访问秒数”,而是“距上次衰减分钟数 + 频次权重”,很多监控脚本直接拿这个值做冷 key 判定,结果全错。
TTL 键混用时 maxmemory-samples 的实际作用范围
当使用 volatile-lru 这类策略时,Redis 会先过滤出所有 ttl > 0 的 key,再在其中执行采样淘汰。关键点在于:
- 一个刚被
EXPIRE设置了 3600 秒的 key,它的lru字段不会重置,仍沿用上次访问时间戳 - 这意味着:哪怕它 TTL 很长,只要最近没被访问,就可能在 volatile-lru 淘汰中被优先抽中
- 所以
maxmemory-samples的实际作用域,取决于你配的是allkeys-*还是volatile-*—— 前者扫全库,后者只扫带 TTL 的子集
真正复杂的地方在于:采样是概率行为,而你的业务流量不是均匀分布的;调大 maxmemory-samples 只能缓解偏差,不能消除。要不要调、调多少,得看 INFO stats 里的 evicted_keys 和 expired_keys 比例,再结合 redis-cli --stat 观察淘汰前后缓存命中率变化——别光盯着参数数字看。











