当前位置: 首页 > news >正文

Redis的零食盒满了怎么办?详解缓存淘汰策略

Redis 缓存过期淘汰策略

第一站:为什么要“淘汰”?(概念铺垫)

首先,我们得明白为什么要费劲心思地设计这些“淘汰策略”?

通俗类比:

想象你的 Redis 就像一个 容量有限的“零食盒”

  1. 过期时间(TTL): 你买了一堆零食,有些零食(数据)是有保质期(TTL,Time To Live,存活时间)的。时间一到,即使盒子没满,这个零食也得扔掉,因为它“过期”了。
  2. 内存限制(Maxmemory): 你的零食盒就这么大(maxmemory 配置),当盒子满了,你又想放新的零食进去,怎么办?你必须先扔掉一些旧的零食腾出空间。

过期策略 解决的是 “时间一到就扔” 的问题。

淘汰策略 解决的是 “盒子满了该扔谁” 的问题。

我们本次主要关注的是第二个问题,即 maxmemory 限制下的 淘汰策略

Redis内存使用
内存是否达到maxmemory?
正常写入数据
触发淘汰策略
根据策略选择淘汰键
删除选中键释放内存
写入新数据

第二站:Redis 的两大基石:过期和淘汰

在介绍具体的淘汰策略前,我们先快速了解一下 Redis 如何处理“过期”这件事,因为它和“淘汰”策略是并存的。

数据的“过期”机制(Expiration)

Redis 处理过期数据的机制,被称为 惰性删除(Lazy Deletion)定期删除(Active Deletion)

A. 惰性删除(Lazy Deletion):随用随清
  • 学术解释: 当客户端尝试访问(GET/MGET 等操作)某个带有 TTL 的键时,Redis 会先检查这个键的过期时间,如果发现它已经过期,则在返回结果前将其删除。
  • 通俗比方: 就像你打开零食盒,拿起一块饼干时,先闻一闻、看一看日期,发现过期了,马上扔掉,而不是放回盒子里。
  • 优点: 节省 CPU 资源,只有被访问时才检查,避免了不必要的扫描。
  • 缺点: 如果大量数据过期后一直没被访问,它们会一直占用内存,直到被淘汰策略处理。
B. 定期删除(Active Deletion):抽样巡检
  • 学术解释: Redis 会周期性地(默认每秒执行 10 次)随机抽取一些设置了 TTL 的键进行检查,并删除其中已过期的键。
    • 这个过程是有限制的,例如每次执行时长不超过 25 毫秒。
  • 通俗比方: 零食盒旁边有个“巡检员”,每隔一段时间(100 毫秒)就随便拿出几包零食看看有没有过期,发现过期就扔掉。
  • 优点: 弥补了惰性删除的不足,可以清理一些不常访问但已过期的数据。
ClientRedis过期键惰性删除流程GET key1检查key1是否过期删除key1返回nil返回valuealt[已过期][未过期]定期删除流程随机抽取20个带TTL的键检查每个键是否过期删除过期键loop[对每个过期键]执行时间≤25msloop[每秒10次]ClientRedis过期键

第三站:缓存淘汰策略(Eviction Policies)

Redis 内存达到 maxmemory 限制,并且有新的数据需要写入时,淘汰策略 就会启动,来决定“牺牲”哪些数据。

Redis 提供了 8 种主要的淘汰策略(自 Redis 4.0 以后):

A. 不淘汰策略(No Eviction)

  • 策略名: noeviction
  • 作用: 当内存不足,且有新的写入命令时,直接返回错误,不会删除任何键。
  • 底层哲学: “宁可报错,不删数据”。适用于那些对数据完整性要求极高,或依赖外部机制保证内存管理的应用。
  • 比方: 零食盒满了,你想放新零食进来,但守卫说:“没地方了,你得等别人吃了或扔了腾出位置再说,我现在不会替你扔任何东西。”

B. 针对“设置了过期时间”的键进行淘汰(Volatile Family)

这组策略只关注那些设置了 TTL 的键。

  1. volatile-lru (Least Recently Used)
    • 作用: 从所有设置了过期时间的键中,淘汰 最近最少使用 的键。
    • 核心思想: 那些长时间没人碰的,以后用到的概率也小,优先淘汰它们。
  2. volatile-lfu (Least Frequently Used)
    • 作用: 从所有设置了过期时间的键中,淘汰 最不经常使用 的键。
    • 核心思想: 相比 LRU 关注“最后一次使用时间”,LFU 更关注“使用次数”。使用次数少的,优先淘汰。
  3. volatile-ttl (Time To Live)
    • 作用: 从所有设置了过期时间的键中,淘汰 剩余 TTL 值最小 的键(即最快要过期的键)。
    • 核心思想: 反正快过期了,不如先扔掉,让过期机制的工作更简单。
  4. volatile-random
    • 作用: 从所有设置了过期时间的键中,随机 淘汰键。
    • 核心思想: 简单粗暴,不求性能最优,只求执行速度快。

C. 针对“所有键”进行淘汰(Allkeys Family)

这组策略会考虑 Redis 数据库中的 所有键,无论是否设置了 TTL。

  1. allkeys-lru (Least Recently Used)
    • 作用:所有键 中,淘汰 最近最少使用 的键。
    • 核心思想: 相比 volatile-lru,它连永久键(没有设置 TTL 的键)也敢删。
  2. allkeys-lfu (Least Frequently Used)
    • 作用:所有键 中,淘汰 最不经常使用 的键。
  3. allkeys-random
    • 作用:所有键 中,随机 淘汰键。
Redis淘汰策略
noeviction
不淘汰
volatile家族
只淘汰有TTL的键
allkeys家族
淘汰所有键
volatile-lru
最近最少使用
volatile-lfu
最不经常使用
volatile-ttl
最快过期
volatile-random
随机淘汰
allkeys-lru
最近最少使用
allkeys-lfu
最不经常使用
allkeys-random
随机淘汰

第四站:底层解剖:LRU 和 LFU 的“近似”实现

LRU 和 LFU 是最常用的淘汰策略,但 Redis 的实现并非是 完全精准 的,而是 近似(Approximation) 的。

1. 为什么是“近似”?

  • 学术解释: 完全精准 的 LRU/LFU 需要维护一个全局有序链表(LRU)或复杂的频率计数结构(LFU)。对于拥有数百万键的 Redis 来说,每次访问或插入/删除都需要移动或更新这些结构,这将带来巨大的 性能开销,完全无法达到 Redis 所追求的高并发、低延迟目标。
  • 通俗比方: 想象你有一千万本图书,要找出“最近最少被借阅”的那一本。如果每借出一本,你都要移动或排序一千万本图书的记录,那图书馆就瘫痪了。

2. Redis 的“近似 LRU” (allkeys-lru / volatile-lru)

Redis 采用的是 随机采样法 来近似实现 LRU。

  • 实现原理:
    1. Redis 维护了一个 24-bit 的字段(lru 字段)记录每个键的 最后一次被访问的时间戳(相对时间)。
    2. 当需要淘汰时,随机选择 少量键(例如,默认配置下选择 maxmemory-samples 个键,默认为 5 个)。
    3. 从这 5 个随机选出的键 中,淘汰 掉那个 lru 字段最小(即最后一次访问时间最久)的键。
  • 比方: 守卫不是挨个检查所有零食,而是 随机抓出 5 个,比较它们的生产日期(最后访问时间),扔掉 这 5 个里面最久远的那个。
  • 总结: 随机采样、局部最优,效率极高,效果接近真正的 LRU。
内存达到maxmemory
随机选择5个键作为候选池
遍历候选池查找LRU字段最小的键
淘汰找到的LRU最小键
释放内存空间
键被访问时
更新该键的LRU字段

3. Redis 的“近似 LFU” (allkeys-lfu / volatile-lfu)

LFU 比 LRU 复杂,它关注的是使用 频率

  • 实现原理:
    1. Redis 使用了一个 24-bit 的字段(lfu 字段)记录每个键的 访问频率。这个字段被分为两部分:
      • 高 8 位记录 访问频率计数器(counter
      • 低 16 位记录 访问时间戳(ltime,用于对频率进行 衰减
    2. 计数器增长: 每次键被访问,counter 都会递增,但不是简单地 +1,而是使用 概率对数计数 的方式,保证高频率的键不会无限增长,而是缓慢趋近一个上限。
    3. 频率衰减: 如果一个键长时间(例如,几分钟)没有被访问,counter 会根据 ltime 的信息自动 衰减,防止长时间不访问的“历史高频键”霸占内存。
  • 比方: 守卫给每包零食贴一个“热度标签”,每次有人拿它,热度就增加一点。但如果长时间没人碰,热度会随着时间自动冷却。淘汰时,就找那些 热度最低 的扔掉。
  • 总结: LFU 通过频率衰减机制,更好地适应了“热点漂移”的情况,即一个键曾经很热,但现在不再使用了,它最终会被淘汰。
初始状态
频繁访问
持续访问
访问减少
长时间未访问
被淘汰
低频键
中频键
高频键
counter值高
但会随时间衰减

第五站:Java 后端技术栈的选择建议

作为 Java 后端开发者,理解这些策略后,如何选择呢?

策略适用场景优点缺点推荐指数
allkeys-lru最常用和推荐的。当你不知道选择什么时,选它。适用于大多数业务场景,如 Session 缓存、热门商品列表等。效果好,命中率高,性能高(近似实现)。可能淘汰掉永不过期但很久没用的关键配置数据。⭐⭐⭐⭐⭐
volatile-lru当你需要将 重要配置或字典数据 设置为永不(或极长 TTL)过期,不希望被淘汰,但又需要缓存大量临时数据时。兼顾了重要数据的保护和缓存淘汰的需求。只能淘汰设置了 TTL 的键。⭐⭐⭐⭐
allkeys-lfu适用于 热点数据分布非常不均匀,且希望长期不被访问的键能比 LRU 更长时间保留 的场景。对高频键的保护比 LRU 更好。略微复杂,计算开销略高于 LRU。⭐⭐⭐
noeviction仅用于 非缓存应用(如作为分布式锁服务)或你完全掌控内存容量,绝不允许数据丢失的场景。保证数据完整性。内存一满立即无法写入。⭐⭐
40%25%20%10%5%Redis淘汰策略使用推荐allkeys-lru (首选推荐)volatile-lru (配置保护)allkeys-lfu (热点场景)noeviction (特殊场景)其他策略

总结:

在绝大多数 Java 缓存场景中,allkeys-lru 是首选。它简单、高效,并且能很好地模拟出热点数据的特性。如果你的 Redis 既做缓存又做配置存储,可以考虑 volatile-lru,将配置数据不设置 TTL 来保护起来。

http://www.dtcms.com/a/441975.html

相关文章:

  • display mac-address vlan vlan-id 概念及题目
  • 国内十大网站建设广州11个区排名
  • windows远程桌面连接的时候用户名用什么
  • Webpack实战笔记:从自动构建到本地服务器搭建的完整流程
  • SpringBoot + MongoDB全栈实战:从架构原理到AI集成
  • 台山网站建设公司申请云应用wordpress
  • 小迪安全v2023学习笔记(九十五讲)—— 云原生篇Docker安全权限环境检测容器逃逸特权模式危险挂载
  • 从零开始的C++学习生活 1:命名空间,缺省函数,函数重载,引用,内联函数
  • react源码
  • 怎么用记事本做钓鱼网站如何做外贸电商
  • 【自学笔记】Redis 快速入门(下篇)
  • 微信网站怎么开发东莞品牌营销型网站建设
  • 在QT中实现线程暂停
  • vivado自定义IP显示只读解决办法
  • 当 AI 走进图像编辑:Bing 照片编辑器的实用价值与体验观察
  • Java Linux --- 基本命令,部署Java web程序到线上访问
  • 天安云谷网站建设企业邮箱忘记密码怎么找回
  • SQL 多表查询场景速查:一对一、一对多、多对多
  • 从 0 到 1 搭建 Python 语言 Web UI自动化测试学习系列 7--基础知识 3--常用函数 1
  • Amazon S3 Vectors:向量存储、索引与多亚马逊云科技服务协同的智能桥梁解决方案
  • 第二章 prompt思维链
  • 大模型面经(一) Prompt + RAG + 微调
  • 第一章——了解prompt以及一些基础技巧方法
  • 做牛津布面料在哪个网站找客户找人一起做素材网站
  • 土豆家族工具使用适配表格大全【windows提权】
  • PyQt5 QPushButton组件详解:按钮控件的完整指南
  • Linux中do_wait函数的实现
  • 第1章 线程安全的对象生命期管理
  • Codeforces Round 1027 A. Square Year (2114)
  • 公司网站备案信息查询嵌入式开发培训