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

java最强本地缓存-Caffeine

前言
👏作者简介:我是笑霸final,一名热爱技术的在校学生。
📝个人主页:个人主页1 || 笑霸final的主页2
📕系列专栏:java专栏
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏

目录

  • 一 简单使用
  • 二、内存驱逐策略
  • 三、存储空间

一 简单使用

添加依赖

<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.5.5</version>
</dependency>

添加策略

Caffeine提供了四种缓存添加策略:
● 手动加载cache
● 自动加载LoadingCache
● 手动异步加载AsyncCache
● 自动异步加载AsyncLoadingCache

  • 手动加载
Cache<Key, Graph> cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).maximumSize(10_000).build();// 查找一个缓存元素, 没有查找到的时候返回null
Graph graph = cache.getIfPresent(key);
// 查找缓存,如果缓存不存在则生成缓存元素,  如果无法生成则返回null
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一个缓存元素
cache.put(key, graph);
// 移除一个缓存元素
cache.invalidate(key);
  • 自动加载
LoadingCache<Key, Graph> cache = Caffeine.newBuilder().maximumSize(10_000).expireAfterWrite(10, TimeUnit.MINUTES).build(key -> createExpensiveGraph(key));// 查找缓存,如果缓存不存在则生成缓存元素,  如果无法生成则返回null
Graph graph = cache.get(key);
// 批量查找缓存,如果缓存不存在则生成缓存元素
Map<Key, Graph> graphs = cache.getAll(keys);
  • 手动异步
AsyncCache<Key, Graph> cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).maximumSize(10_000).buildAsync();// 查找一个缓存元素, 没有查找到的时候返回null
CompletableFuture<Graph> graph = cache.getIfPresent(key);
// 查找缓存元素,如果不存在,则异步生成
graph = cache.get(key, k -> createExpensiveGraph(key));
// 添加或者更新一个缓存元素
cache.put(key, graph);
// 移除一个缓存元素
cache.synchronous().invalidate(key);
  • 自动异步
AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder().maximumSize(10_000).expireAfterWrite(10, TimeUnit.MINUTES)// 你可以选择: 去异步的封装一段同步操作来生成缓存元素.buildAsync(key -> createExpensiveGraph(key));// 你也可以选择: 构建一个异步缓存元素操作并返回一个future.buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));// 查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Graph> graph = cache.get(key);
// 批量查找缓存元素,如果其不存在,将会异步进行生成
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);

二、内存驱逐策略

Caffeine 提供了三种驱逐策略,分别是基于容量,基于时间和基于引用三种类型

2.1基于内容

缓存将会尝试通过基于【Window TinyLfu】驱逐掉不会再被使用到的元素
内容也有两种实现:
● 基于个数:如果你的缓存容量不希望超过某个特定的大小,那么记得使用Caffeine.maximumSize(long)
● 基于权重:你的缓存可能中的元素可能存在不同的“权重”–打个比方,你的缓存中的元素可能有不同的内存占用–你也许需要借助Caffeine.weigher(Weigher) 方法来界定每个元素的权重并通过 Caffeine.maximumWeight(long)方法来界定缓存中元素的总权重来实现上述的场景。

2.2基于时间

基于时间也有三种实现:
● expireAfterAccess(long, TimeUnit): 一个元素在上一次读写操作后一段时间之后,在指定的时间后没有被再次访问将会被认定为过期项。理想选择: 当session因为不活跃而使元素过期的情况下使用此】

● expireAfterWrite(long, TimeUnit): 一个元素将会在其创建或者最近一次被更新之后的一段时间后被认定为过期项。理想选择: 对被缓存的元素的时效性存在要求的场景下使用此】

● expireAfter(Expiry): 一个元素将会在指定的时间后被认定为过期项。理想选择: 当被缓存的元素过期时间收到外部资源影响的时候使用此】。

2.3基于引用

注意:
● 异步不支持
● key支持:弱引用
● value支持:软引用、弱引用。

基于引用也有三种实现:
● Caffeine.weakKeys() 在保存key的时候将会进行弱引用。这允许在GC的过程中,当key没有被任何强引用指向的时候去将缓存元素回收
● Caffeine.weakValues()在保存value的时候将会使用弱引用。这允许在GC的过程中,当value没有被任何强引用指向的时候去将缓存元素回收
● Caffeine.softValues()在保存value的时候将会使用软引用。为了相应内存的需要,在GC过程中被软引用的对象将会被通过LRU算法回收。由于使用软引用可能会影响整体性能,我们还是建议通过使用基于缓存容量的驱逐策略代替软引用的使用

三、存储空间

3.1构成

W-TinyLFU将缓存存储空间分为两个大的区域:Window Cache(1%)Main Cache(99%).
● Main Cache[SLRU(Segmented LRU,即分段 LRU)]进一步划分为Protected Cache(保护区 80%)和Probation Cache(考察区 20%)两个区域,这两个区域都是基于LRU的Cache。
● 猜测:1%、99%(20%、80%)这样的配置应该是实验得来。不过这个比例 Caffeine 会在运行时根据统计数据(statistics)去动态调整在这里插入图片描述

3.2写入机制

在这里插入图片描述在这里插入图片描述
● 当有新的缓存项写(item)入缓存时,会先写入Window Cache区域,当Window Cache空间满时,最旧的缓存项(根据LRU) 会被移出Window Cache,放到 probation(观察) 区。
● 如果 probation 区也满了,就把 item 和 probation 将要淘汰的元素 victim,两个进行“PK”【TinyLFU算法】,胜者留在 probation,输者就要被淘汰了。
● Probation(观察区) 中的缓存项如果访问频率达到一定次数,会提升到Protected(保护区);
● 如果Protected也满了,最旧的缓存项也会移出Protected Cache,然后根据TinyLFU算法确定是丢弃(淘汰)还是写入Probation Cache。

  • 【tips】每一个缓存项,都由 2 部分组成
    ● 数据:缓存数据本身;
    ● 访问频次:被访问的次数

3.3 TinyLFU淘汰算法

在这里插入图片描述
【 win Cache和protected cache淘汰出来的数据称为 candidate。Probation淘汰出来的数据称为victim】
这 3 个区域移出的缓存项都是各自区域中最久未被使用的数据,在 TinyLFU 过滤器中进行竞争,竞争算法如下:

  • 如果 Candidate 的访问频率 > Victim 的访问频率,则直接淘汰 Victim ;
  • 如果 Candidate 的访问频率 <= Victim 的访问频率,此时分为两种情况::
    》 如果 Candidate 的访问频率 < 5 ,则淘汰 Candidate ;
    》 如果 Candidate 的访问频率 >= 5 ,则在 Candidate 和 Victim 中随机淘汰一个。

频率计算:使用 Count-Min Sketch 算法存储访问频率,极大地节省了空间。
● 类似于布隆过滤器,多次hash,但是数值不仅仅是0 1了,多次hash取值最低的当做访问频率。
● 值最大为4位,也就是15。
● 频度也不是一直不变,为了让缓存降低新鲜度。当整体的统计计数(当前所有记录的频率统计之和,这个数值内部维护)达到某一个值时,那么所有记录的频率统计除以 2。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 「数组」随机快速选择 / LeetCode LCR 076(C++)
  • VMWare虚拟机磁盘扩容
  • 【langchain学习】使用LangChain创建具有上下文感知的问答系统
  • 微服务-分布式事务-seata
  • 神经网络:智能时代的基石
  • 再分享API形式调用Dify项目应用
  • 大语言模型生成无人系统(如机械臂、无人机等)可以执行的指令序列
  • 初学嵌入式-C语言常犯错误详解
  • 【机器学习之深度学习】Sigmoid和ReLU的联系与区别、ReLU如何解决死亡问题以及Tanh激活函数的基本概念
  • ClickHouse:单机安装
  • 【数据结构】—— 队列
  • 阿里大模型调用 = 》通义千问大语言模型
  • GenAI下沉到边缘侧,内存和性能如何平衡?
  • 江科大/江协科技 STM32学习笔记P22
  • 四数之和(LeetCode)
  • 《深入 React 技术栈》
  • CSS3 变换
  • Git学习与使用心得(1)—— 初始化
  • javascript从右向左截取指定位数字符的3种方法
  • leetcode讲解--894. All Possible Full Binary Trees
  • MD5加密原理解析及OC版原理实现
  • Mysql5.6主从复制
  • Netty源码解析1-Buffer
  • Python学习笔记 字符串拼接
  • Spark RDD学习: aggregate函数
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • ubuntu 下nginx安装 并支持https协议
  • 工程优化暨babel升级小记
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 小而合理的前端理论:rscss和rsjs
  • 学习使用ExpressJS 4.0中的新Router
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​iOS实时查看App运行日志
  • !$boo在php中什么意思,php前戏
  • #Datawhale AI夏令营第4期#多模态大模型复盘
  • #单片机(TB6600驱动42步进电机)
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (173)FPGA约束:单周期时序分析或默认时序分析
  • (Git) gitignore基础使用
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (SpringBoot)第七章:SpringBoot日志文件
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (利用IDEA+Maven)定制属于自己的jar包
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (一)eclipse Dynamic web project 工程目录以及文件路径问题
  • (一)插入排序
  • (一)为什么要选择C++
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转)VC++中ondraw在什么时候调用的
  • (转)关于如何学好游戏3D引擎编程的一些经验