谈谈 Redis 的内存淘汰策略?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

Redis 的内存淘汰策略?Redis 的内存淘汰策略?

面试考察点

面试官问出这个问题,通常想考察以下几个核心点:

  1. 对缓存系统核心机制的理解:考察你是否理解当缓存内存用尽时,系统如何处理写入请求这一关键问题。
  2. 对具体策略的掌握程度:不仅仅是背诵策略名称,更是想知道你能否清晰阐述每种策略的定义、工作原理及区别。
  3. 技术选型与场景结合的能力:面试官最想听到的是,你能根据不同的业务场景和数据访问模式,分析并选择最合适的淘汰策略。
  4. 实践与配置经验:你是否了解这些策略在 Redis 中如何配置,以及在实际生产环境中的注意事项。
  5. 延伸思考能力:能否将 Redis 的内存淘汰与 JVM 垃圾回收或其他系统的缓存策略进行类比或区分,体现知识的广度。

核心答案

当 Redis 所使用的内存达到 maxmemory 配置的阈值时,便会触发内存淘汰。Redis 提供了 8 种策略,可通过 maxmemory-policy 配置,主要分为三类:

  1. 不淘汰
    • noeviction:默认策略。不淘汰任何键,当内存不足时,新写入操作会报错。
  2. 在所有键中淘汰 (allkeys-*):
    • allkeys-lru:从所有键中,淘汰最近最少使用的键。
    • allkeys-lfu:从所有键中,淘汰最不经常使用的键。
    • allkeys-random:从所有键中,随机淘汰。
  3. 在设置了过期时间的键中淘汰 (volatile-*):
    • volatile-lru:从设置了过期时间的键中,淘汰最近最少使用的键。
    • volatile-lfu:从设置了过期时间的键中,淘汰最不经常使用的键。
    • volatile-random:从设置了过期时间的键中,随机淘汰。
    • volatile-ttl:从设置了过期时间的键中,淘汰剩余生存时间最短的键。

深度解析

原理/机制

  • LRU(最近最少使用):传统 LRU 需要维护一个链表,移动开销大。Redis 采用了一种近似 LRU 算法,通过随机采样(默认5个键)并淘汰其中最久未使用的,在性能和精度上取得了平衡。
  • LFU(最不经常使用):从 Redis 4.0 开始引入。LFU 会统计每个键的访问频率,并优先淘汰频率最低的键。为了避免早期的高频访问记录永久影响,Redis 的 LFU 实现了访问次数衰减机制,随着时间的推移,计数会逐渐减少。
  • TTL(生存时间):这个策略非常直接,它认为即将过期的数据价值更低,应优先被清理。

对比分析与最佳实践

选择哪种策略,完全取决于你的数据特征和业务场景:

策略适用场景注意事项
noeviction数据绝对不能丢失,且你能确保内存永远够用(或通过其他方式扩容)。生产环境慎用,除非有完善的监控和告警,否则易导致写入失败,服务不可用。
allkeys-lru最常用。业务数据访问符合“二八法则”,即部分热点数据被频繁访问。如果你的数据没有明显的冷热区分,效果可能不理想。
allkeys-lfu访问频率的差异比访问时间的远近更能区分数据价值。例如,某键一天内被短时间密集访问后永不再用,LFU 比 LRU 能更快地将其淘汰。适用于对长期热点和短期爆点的区分有要求的场景。
volatile-lru/ttl系统中同时存在永久数据缓存数据。你只想淘汰缓存数据(设置了 TTL 的),而永久数据必须保留。必须为缓存数据正确设置过期时间,否则它们永远不会被纳入淘汰范围,可能导致内存堆积。
allkeys-random所有键被访问的概率几乎相等,没有明显热点。淘汰最不可控,通常不作为首选。
volatile-lfu在设置了 TTL 的缓存数据中,需要根据访问频率来淘汰。volatile-lru,需确保缓存数据有 TTL。

常见误区

  1. 认为 volatile-* 策略只淘汰“已过期”的键:错。它淘汰的是设置了过期时间且未过期的键。过期键的清理是 Redis 的定期删除惰性删除机制负责的,与内存淘汰是两回事。
  2. 混淆 LRU 和 LFU:LRU 关注“何时访问”,淘汰 “最老” 的访问记录;LFU 关注 “访问次数”,淘汰 “最不活跃” 的键。一个很久前被访问多次的键,在 LRU 看来可能很 “冷”,但在 LFU 看来可能依然很 “热”。
  3. 忘记设置 maxmemory:如果不设置最大内存,Redis 将不会触发内存淘汰,直到耗尽所有系统内存,可能引发 OOM 导致进程被杀。

总结

Redis 的内存淘汰策略是一套精细的内存管理工具,核心思想是在内存受限时,根据不同的数据价值评估算法(如 LRU、LFU、TTL)来做出取舍,没有最好的策略,只有最适合业务场景的策略。通常,allkeys-lru 是一个稳健的通用选择。