真实面经题目 · 原创解析
Redis 缓存淘汰策略有哪些?
这题要先区分两个概念:过期删除是针对设置了 TTL 的键,缓存淘汰是 Redis 在内存达到 maxmemory 限制后,为了腾出空间而选择删除哪些键。回答时不能只背策略名称,还要说明 allkeys 与 volatile 的区别、LRU/LFU/TTL/random/noeviction 的选择逻辑,以及这些策略在读多写多、冷热数据明显、TTL 管理严格等场景下的取舍。
真实面经题目 · 原创解析
这题要先区分两个概念:过期删除是针对设置了 TTL 的键,缓存淘汰是 Redis 在内存达到 maxmemory 限制后,为了腾出空间而选择删除哪些键。回答时不能只背策略名称,还要说明 allkeys 与 volatile 的区别、LRU/LFU/TTL/random/noeviction 的选择逻辑,以及这些策略在读多写多、冷热数据明显、TTL 管理严格等场景下的取舍。
Redis 的淘汰要分成过期键删除和内存淘汰两层来看。过期删除处理的是设置了 TTL 的 key,Redis 会通过惰性删除和定期删除来清理过期数据;而缓存淘汰发生在配置了 maxmemory,并且写入新数据导致内存超限时。淘汰策略主要有 noeviction、allkeys-lru、volatile-lru、allkeys-lfu、volatile-lfu、allkeys-random、volatile-random、volatile-ttl。allkeys 表示从所有 key 中选,volatile 表示只从设置了过期时间的 key 中选;lru 倾向淘汰最近最少使用的数据,lfu 倾向淘汰访问频率最低的数据,random 是随机淘汰,ttl 是优先淘汰剩余生存时间更短的 key,noeviction 则不淘汰,写入会报错。工程上如果 Redis 主要作为缓存,一般更常用 allkeys-lru 或 allkeys-lfu;如果混合保存不能被淘汰的数据,就需要用 volatile 系列并保证可淘汰数据都设置 TTL。判断选型时要看数据是否都有过期时间、是否存在明显热点、能否接受写入失败,以及内存打满时希望保护哪些数据。
过期删除不是等同于缓存淘汰。过期删除针对的是设置了 TTL 的键,键到期后逻辑上不可再被使用,但 Redis 不会在到期瞬间逐个删除,而是通过访问时惰性检查和后台定期抽样清理来降低开销。内存淘汰则发生在 maxmemory 被触发之后,尤其是写命令需要申请更多内存时,Redis 才按照 maxmemory-policy 选择要移除的键。前者解决数据生命周期,后者解决内存容量压力,两者可能同时存在,但触发条件和目标不同。
策略名称里的 allkeys 和 volatile 决定了候选集合。allkeys 表示所有键都可能被淘汰,适合 Redis 只承担缓存角色,数据可以从数据库或服务端重新构建。volatile 表示只在设置了过期时间的键里选择淘汰对象,适合一个实例里既有缓存数据,也有不希望被淘汰的状态数据。不过 volatile 系列有一个边界:如果没有足够带 TTL 的键可淘汰,写入仍可能失败或无法有效释放内存,所以它要求业务严格给可淘汰数据设置 TTL。
LRU 是最近最少使用,适合访问局部性明显的场景,比如最近被访问的数据短期内大概率还会被访问。LFU 是最少频率使用,适合长期热点比较稳定的场景,可以减少偶发扫描流量把老热点挤掉的问题。random 是随机淘汰,成本低但命中率不可控,通常只在对命中率不敏感或数据价值接近时考虑。volatile-ttl 会优先淘汰剩余过期时间更短的键,适合 TTL 本身能表达数据价值或时效性的场景。
noeviction 不是不控制内存,而是在内存达到限制后拒绝会增加内存的写入命令,返回错误,已有数据尽量保留。它适合 Redis 保存关键状态、分布式锁、限流计数、任务状态等不能随便丢的数据场景。代价是上游必须能处理写入失败,否则内存打满会变成业务错误。对于纯缓存场景,noeviction 往往不是理想选择,因为缓存本来应该通过淘汰换取可用性和吞吐,而不是把压力直接抛给业务写路径。
如果实例只放可重建缓存,优先考虑 allkeys-lru 或 allkeys-lfu;如果热点长期稳定,LFU 往往比 LRU 更抗偶发批量访问;如果热点随时间变化快,LRU 更容易跟随近期访问模式。如果实例混放缓存和不可丢数据,需要考虑 volatile 系列,但这通常也提示应该评估实例拆分,因为混放会让内存治理复杂化。若业务强依赖 TTL 表达时效,volatile-ttl 可以让快过期的数据先出局,但它不一定能带来最高缓存命中率。
Redis 的 LRU 和 LFU 都不是为每次淘汰维护一个绝对精确的全局排序结构,而是通过抽样和近似元数据来控制成本。这样做能避免每次访问或淘汰都引入过大的维护开销,但也意味着被淘汰的键是统计意义上的较差候选,不保证一定是全局最久未访问或频率最低的键。实际调优时不能只看策略名称,还要观察命中率、淘汰数量、内存碎片、写入延迟和业务回源压力。
如果追问如何判断瓶颈是不是带宽问题,可以从 Redis 自身指标、机器网卡指标和业务延迟三方面交叉验证。重点看输入输出吞吐是否接近网卡上限、瞬时流量是否与慢请求和超时同步升高、Redis CPU 是否并未打满但网络队列或客户端等待变长。如果大 key、大批量 mget、热 key 高并发读取导致出流量异常,带宽瓶颈的概率会升高;如果 CPU、内存淘汰、磁盘持久化或命令复杂度先异常,则不能把问题简单归因于带宽。
过期删除是清理已经超过 TTL 的键,触发方式包括访问时惰性检查和后台定期扫描。内存淘汰是在达到 maxmemory 后,为新写入腾空间而删除某些键。一个关注时间生命周期,一个关注容量压力。
纯缓存实例里的数据通常都可以回源重建,所以从所有键中挑选淘汰对象更直接。LRU 能适应近期热点,LFU 能保护长期高频热点。相比 volatile 系列,它们不依赖每个键都正确设置 TTL。
volatile-ttl 会优先淘汰剩余生存时间更短的键,适合 TTL 能表达数据价值或时效性的场景,比如越接近过期的数据越不值得保留。但它不一定带来最高命中率,因为剩余时间短不等于访问价值低。
如果策略找不到合适的淘汰对象,或者使用 noeviction,新增写入可能会失败并返回错误。已有读请求通常还能继续,但写路径会受影响。因此业务要么保证候选键充足,要么能处理写入失败。
要同时看 Redis 的输入输出吞吐、机器网卡利用率、客户端等待时间和 CPU 使用率。如果网络吞吐接近上限、出入流量高峰与超时同步,而 CPU 不高,才更像带宽瓶颈。还要排查大 key 和批量读取。