本地缓存Caffeine

Caffeine

说起Guava Cache,很多人都不会陌生,它是Google Guava工具包中的一个非常方便易用的本地化缓存实现,基于LRU算法实现,支持多种缓存过期策略。由于Guava的大量使用,Guava Cache也得到了大量的应用。但是,Guava Cache的性能一定是最好的吗?也许,曾经,它的性能是非常不错的。但所谓长江后浪推前浪,总会有更加优秀的技术出现。今天,我就来介绍一个比Guava Cache性能更高的缓存框架:Caffeine。

Tips: Spring5(SpringBoot2)开始用Caffeine取代guava.详见官方信息SPR-13797 jira.spring.io/browse/SPR-…

什么时候用

  1. 愿意消耗一些内存空间来提升速度
  2. 预料到某些键会被多次查询
  3. 缓存中存放的数据总量不会超出内存容量

性能

由图可以看出,Caffeine不论读还是写的效率都远高于其他缓存。

这里只列出部分性能比较,详细请看官方官方 github.com/ben-manes/c…

依赖

我们需要在 pom.xml 中添加 caffeine 依赖:

版本问题参考mvnrepository.com/artifact/co…

<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.7.0</version>
</dependency>
复制代码

新建对象

// 1、最简单
Cache<String, Object> cache = Caffeine.newBuilder().build();
// 2、真实使用过程中我们需要自己配置参数。这里只列举部分,具体请看下面列表
Cache<String, Object> cache = Caffeine.newBuilder().initialCapacity(2)//初始大小.maximumSize(2)//最大数量.expireAfterWrite(3, TimeUnit.SECONDS)//过期时间.build();
复制代码

参数含义

  • initialCapacity: 初始的缓存空间大小
  • maximumSize: 缓存的最大数量
  • maximumWeight: 缓存的最大权重
  • expireAfterAccess: 最后一次读或写操作后经过指定时间过期
  • expireAfterWrite: 最后一次写操作后经过指定时间过期
  • refreshAfterWrite: 创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存
  • weakKeys: 打开key的弱引用
  • weakValues:打开value的弱引用
  • softValues:打开value的软引用
  • recordStats:开发统计功能

注意: expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。 maximumSize和maximumWeight不可以同时使用

异步

AsyncCache<Object, Object> asyncCache = Caffeine.newBuilder().buildAsync();
复制代码

解释

A semi-persistent mapping from keys to values. Cache entries are manually added using
{@link #get(Object, Function)} or {@link #put(Object, CompletableFuture)}, and are stored in the
cache until either evicted or manually invalidated.
Implementations of this interface are expected to be thread-safe, and can be safely accessed by
multiple concurrent threads.
复制代码

添加数据

Caffeine 为我们提供了三种填充策略:

手动、同步和异步

手动添加

很简单的

public static void main(String[] args) {Cache<String, String> cache = Caffeine.newBuilder().build();cache.put("hello", "world");System.out.println(cache.getIfPresent("hello"));
}
复制代码

自动添加1(自定义添加函数)

Cache<String, String> cache = Caffeine.newBuilder().build();// 1.如果缓存中能查到,则直接返回
// 2.如果查不到,则从我们自定义的getValue方法获取数据,并加入到缓存中
cache.get("hello", new Function<String, String>() {@Overridepublic String apply(String k) {return getValue(k);}
});
System.out.println(cache.getIfPresent("hello"));
}// 缓存中找不到,则会进入这个方法。一般是从数据库获取内容
private static String getValue(String k) {return k + ":value";
复制代码

// 这种写法可以简化成下面Lambda表达式 cache.get("hello", new Function<String, String>() { @Override public String apply(String k) { return getValue(k); } }); // 可以简写为 cache.get("hello", k -> getValue(k));

自动添加2(初始添加)

和上面方法一样,只不过这个是在新建对象的时候添加

LoadingCache<String, String> loadingCache = Caffeine.newBuilder().build(new CacheLoader<String, String>() {@Overridepublic String load(String k) {return getValue(k);}});
// 同样可简化为下面这样
LoadingCache<String, String> loadingCache2 = Caffeine.newBuilder().build(k -> getValue(k));
复制代码

过期策略

Caffeine提供三类驱逐策略:

  1. 基于大小(size-based)
  2. 基于时间(time-based)
  3. 基于引用(reference-based)

1、大小

Cache<String, String> cache = Caffeine.newBuilder().maximumSize(3).build();
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.put("key3", "value3");
cache.put("key4", "value4");
cache.put("key5", "value5");
cache.cleanUp();
System.out.println(cache.getIfPresent("key1"));
System.out.println(cache.getIfPresent("key2"));
System.out.println(cache.getIfPresent("key3"));
System.out.println(cache.getIfPresent("key4"));
System.out.println(cache.getIfPresent("key5"));
复制代码

输出结果

null
value2
null
value4
value5
复制代码

1、淘汰2个

2、淘汰并不是按照先后顺序,内部有自己的算法

2、时间

Caffeine提供了三种定时驱逐策略:

  • expireAfterAccess(long, TimeUnit):在最后一次访问或者写入后开始计时,在指定的时间后过期。假如一直有请求访问该key,那么这个缓存将一直不会过期。
  • expireAfterWrite(long, TimeUnit): 在最后一次写入缓存后开始计时,在指定的时间后过期。
  • expireAfter(Expiry): 自定义策略,过期时间由Expiry实现独自计算。

缓存的删除策略使用的是惰性删除和定时删除。这两个删除策略的时间复杂度都是O(1)。

expireAfterWrite

Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).build();
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.put("key3", "value3");
cache.put("key4", "value4");
cache.put("key5", "value5");
System.out.println(cache.getIfPresent("key1"));
System.out.println(cache.getIfPresent("key2"));
Thread.sleep(3*1000);
System.out.println(cache.getIfPresent("key3"));
System.out.println(cache.getIfPresent("key4"));
System.out.println(cache.getIfPresent("key5"));
复制代码

结果

value1
value2
null
null
null
复制代码

例子2

Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).build();
cache.put("key1", "value1");
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
复制代码

结果

value1
value1
null
复制代码

expireAfterAccess

Access就是读和写

Cache<String, String> cache = Caffeine.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS).build();
cache.put("key1", "value1");
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("key1"));
Thread.sleep(3*1000);
System.out.println(cache.getIfPresent("key1"));
复制代码

结果

value1
value1
value1
null
复制代码

读和写都没有的情况下,3秒后才过期

也可以同时用expireAfterAccess和expireAfterWrite方法指定过期时间,这时只要对象满足两者中的一个条件就会被自动过期删除。

expireAfter 和 refreshAfter 之间的区别

  • expireAfter 条件触发后,新的值更新完成前,所有请求都会被阻塞,更新完成后其他请求才能访问这个值。这样能确保获取到的都是最新的值,但是有性能损失。
  • refreshAfter 条件触发后,新的值更新完成前也可以访问,不会被阻塞,只是获取的是旧的数据。更新结束后,获取的才是新的数据。有可能获取到脏数据。

3、引用

  • Caffeine.weakKeys() 使用弱引用存储key。如果没有其他地方对该key有强引用,那么该缓存就会被垃圾回收器回收。
  • Caffeine.weakValues() 使用弱引用存储value。如果没有其他地方对该value有强引用,那么该缓存就会被垃圾回收器回收。
  • Caffeine.softValues() 使用软引用存储value。
Cache<String, Object> cache = Caffeine.newBuilder().weakValues().build();
Object value1 = new Object();
Object value2 = new Object();
cache.put("key1", value1);
cache.put("key2", value2);value2 = new Object(); // 原对象不再有强引用
System.gc();
System.out.println(cache.getIfPresent("key1"));
System.out.println(cache.getIfPresent("key2"));
复制代码

结果

java.lang.Object@7a4f0f29
null
复制代码

解释:当给value2引用赋值一个新的对象之后,就不再有任何一个强引用指向原对象。System.gc()触发垃圾回收后,原对象就被清除了。

简单回顾下Java中的四种引用

Java4种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用

引用类型被垃圾回收时间用途生存时间
强引用从来不会对象的一般状态JVM停止运行时终止
软引用在内存不足时对象缓存内存不足时终止
弱引用在垃圾回收时对象缓存GC运行后终止
虚引用UnknownUnknownUnknown

显式删除缓存

除了通过上面的缓存淘汰策略删除缓存,我们还可以手动的删除

// 1、指定key删除
cache.invalidate("key1");
// 2、批量指定key删除
List<String> list = new ArrayList<>();
list.add("key1");
list.add("key2");
cache.invalidateAll(list);//批量清除list中全部key对应的记录
// 3、删除全部
cache.invalidateAll();
复制代码

淘汰、移除监听器

可以为缓存对象添加一个移除监听器,这样当有记录被删除时可以感知到这个事件。

Cache<String, String> cache = Caffeine.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS).removalListener(new RemovalListener<Object, Object>() {@Overridepublic void onRemoval(@Nullable Object key, @Nullable Object value, @NonNull RemovalCause cause) {System.out.println("key:" + key + ",value:" + value + ",删除原因:" + cause);}}).expireAfterWrite(1, TimeUnit.SECONDS).build();
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.invalidate("key1");
Thread.sleep(2 * 1000);
cache.cleanUp();
复制代码

结果

key:key1,value:value1,删除原因:EXPLICIT
key:key2,value:value2,删除原因:EXPIRED
复制代码

统计

Cache<String, String> cache = Caffeine.newBuilder().maximumSize(3).recordStats().build();
cache.put("key1", "value1");
cache.put("key2", "value2");
cache.put("key3", "value3");
cache.put("key4", "value4");cache.getIfPresent("key1");
cache.getIfPresent("key2");
cache.getIfPresent("key3");
cache.getIfPresent("key4");
cache.getIfPresent("key5");
cache.getIfPresent("key6");
System.out.println(cache.stats());
复制代码

结果

CacheStats{hitCount=4, missCount=2, loadSuccessCount=0, loadFailureCount=0, totalLoadTime=0, evictionCount=0, evictionWeight=0}
复制代码

除了结果输出的内容,CacheStats还可以获取如下数据。

参考

oopsguy.com/2017/10/25/… juejin.im/post/5b8df6… www.jianshu.com/p/9a80c662d… www.sohu.com/a/235729991… www.cnblogs.com/yueshutong/… blog.csdn.net/qq_38974634… blog.csdn.net/qq_32867467… blog.csdn.net/grafx/artic… ifeve.com/google-guav…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/536450.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《图解HTTP》核心知识总结

HTTP协议的简介 HTTP是超文本传输协议&#xff0c;用于客户端和服务器端之间的通信&#xff0c;属于TCP/IP中的应用层。 HTTP协议的基础知识 客户端和服务器端 客户端是服务请求方&#xff0c;服务器端是服务提供方。 URI和URL URI:URI是统一资源标识符&#xff1b; URL:是统一…

1042: 筛法求素数

1042: 筛法求素数 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 1387 Solved: 918 [Submit][Status][Web Board] Description 用筛法求之N内的素数。 Input N Output 0&#xff5e;N的素数 Sample Input 100 Sample Output 2 3 5 7 11 13 17 19 23 29 31 37 4…

状态机解析请求行

微信公众号&#xff1a;郑尔多斯关注「郑尔多斯」公众号 &#xff0c;回复「领取资源」&#xff0c;获取IT资源500G干货。升职加薪、当上总经理、出任CEO、迎娶白富美、走上人生巅峰&#xff01;想想还有点小激动关注可了解更多的Nginx知识。任何问题或建议&#xff0c;请公众号…

GO 从零开始的语法学习二

for循环 if条件里不需要括号 err ! nil 判断是否为空 func main(){const filename "abc.txt"contents , err : ioutil.ReadFile(filename); err ! nil{fmt.Println(err)} else{fmt.Printf("%s\n",contents)} } 复制代码if的条件里可以进行赋值if的条件里…

7个有用的Vue开发技巧

1 状态共享 随着组件的细化&#xff0c;就会遇到多组件状态共享的情况&#xff0c;Vuex当然可以解决这类问题&#xff0c;不过就像Vuex官方文档所说的&#xff0c;如果应用不够大&#xff0c;为避免代码繁琐冗余&#xff0c;最好不要使用它&#xff0c;今天我们介绍的是vue.js …

Kewail-邮件短信接口的基础教程

短信接口接入流程开始接入手机短信接口接入操作流程&#xff1a;申请短信签名 → 申请短信模板 → 生成AccessKey → 下载DEMO/攒写接口调用文档 → 免费测试发送 → 购买发信量正式使用。一、申请短信签名接入API接口&#xff0c;通过1069通道发送验证码等短信&#xff0c;必须…

传百度无人车计划分拆,百度回复:不实信息,目前未有分拆计划

据《财经》报道&#xff0c;百度无人车项目正在筹备分拆(spin off)当中&#xff0c;且正在寻找外部投资机构融资。一位接近百度无人车项目人士对《财经》表明&#xff0c;分拆就是时间问题。对于无人车项目分拆一事&#xff0c;百度对 36 氪表示&#xff0c;媒体报道不实。目前…

又见回文

又见回文 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description “回文串”是一个正读和反读都一样的字符串&#xff0c;比如“level”或者“noon”等等就是回文串。现在呢&#xff0c;就是让你判断输入的字符串是否是回文串。 Inpu…

Fighting_小银考呀考不过四级【递推】

Fighting_小银考呀考不过四级 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description 四级考试已经过去好几个星期了&#xff0c;但是小银还是对自己的英语水平担心不已。 小银打算好好学习英语&#xff0c;争取下次四级考试和小学弟小…

从xml中返回的对象,和new 返回的对象时不同的。

public BigDecimal getTax() {return tax null ? BigDecimal.ZERO : tax;} 这是自定义的一个类 对null 做出了处理。 但是如果是直接从xml 查询返回的该对象&#xff0c; tax() 字段还是会产生null <resultMap id"twoToNine" type"" ><result …

三国佚事——巴蜀之危【递推】

三国佚事——巴蜀之危 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description 话说天下大势&#xff0c;分久必合&#xff0c;合久必分。。。却道那魏蜀吴三国鼎力之时&#xff0c;多少英雄豪杰以热血谱写那千古之绝唱。古人诚不我欺…

HTTP Authentication(HTTP认证)(转)

HTTP协议规范中有两种认证方式&#xff0c;一种是Basic认证&#xff0c;另外一种是Digest认证&#xff0c;这两种方式都属于无状态认证方式&#xff0c;所谓无状态即服务端都不会在会话中记录相关信息&#xff0c;客户端每次访问都需要将用户名和密码放置报文一同发送给服务端&…

们--加强斐波那契【递推】

们--加强斐波那契 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description 对于斐波那契数列想必各位已经见过了。这里给出一个加强版。 F[i] i (i < 3); F[i] F[i-1] F[i-2] F[i-3](i > 4); Input 多组输入。每组输入一…

inux CentOS 7 修改内核启动默认顺序

2019独角兽企业重金招聘Python工程师标准>>> inux CentOS 7 修改内核启动默认顺序 2018年12月07日 09:53:32 XueShengke 阅读数&#xff1a;781 转载于&#xff1a;21运维 Linux CentOS 7.X 如何修改内核启动默认顺序 我们知道&#xff0c;centos 6.x是通过/etc/gr…

快速掌握ajax!

ajax是什么&#xff1f;ajax——asynchronous JavaScript and xml&#xff1a;异步的js和xml它能使用js访问服务器&#xff0c;而且是异步访问服务器给客户端的响应一般是整个页面&#xff0c;一个html完整页面&#xff01;但在ajax中因为是局部刷新&#xff0c;那么服务器就不…

锁底层之内存屏障与原语指令

Java内存模型1&#xff0e;工作内存和主内存Java内存模型规定所有的变量都存储在主内存中&#xff08;JVM内存的一部分&#xff09;&#xff0c;每个线程有自己独立的工作内存&#xff0c;它保存了被该线程使用的变量的主内存复制。线程对这些变量的操作都在自己的工作内存中进…

微信点击链接,用默认浏览器中打开指定网址链接!

2019独角兽企业重金招聘Python工程师标准>>> 最近有客户咨询&#xff0c;自己的链接在微信种推广&#xff0c;经常会被无缘无故封杀&#xff0c;有没有一种功能&#xff0c;用户在微信中点击我们推广的链接&#xff0c;可以自动强制跳转到手机默认浏览器中打开指定的…

elasticsearch存储空间不足导致索引只读,不能创建

问题描述 1.添加数据时&#xff0c;报错&#xff0c;原因是&#xff0c;一旦在存储超过95&#xff05;的磁盘中的节点上分配了一个或多个分片的任何索引&#xff0c; 该索引将被强制进入只读模式 ClusterBlockException[blocked by: [FORBIDDEN/12/index read-only / allow del…

java版spring cloud+spring boot 社交电子商务平台:服务消费(基础)

使用LoadBalancerClientSpring cloud b2b2c电子商务社交平台源码请加企鹅求求&#xff1a;一零三八七七四六二六。在Spring Cloud Commons中提供了大量的与服务治理相关的抽象接口&#xff0c;包括DiscoveryClient、这里我们即将介绍的LoadBalancerClient等。对于这些接口的定义…

Monthly Expense【二分】

B - Monthly Expense POJ - 3273 Farmer John is an astounding accounting wizard and has realized he might run out of money to run the farm. He has already calculated and recorded the exact amount of money (1 ≤ moneyi ≤ 10,000) that he will need to spend …