简介
Caffeine 是一款基于 jdk8 实现的缓存工具,在设计上参考了 google 的 Guava cach 组件,可以理解为是一个 GuavaCache 的加强版本。
适用场景
因为 Caffeine cache 是类似于 Guava cache 的一种内存缓存,所以适合单机的数据缓存;因为存储在内存的,没有持久化,因此适合一些短期或者启动以及结果信息的短暂缓存。当涉及到多机多服务的缓存时候,属于分布式缓存的范畴,可以使用 Redis、memcached 等分布式的缓存组件。
缓存填充策略
手动
手动加载:手动控制缓存的增删改处理,主动增加、获取以及依据函数式更新缓存;底层使用 ConcurrentHashMap 进行节点存储,因此 get () 方法是安全的。批量查找可以使用 getAllPresent () 方法或者带填充默认值的 getAll () 方法。
1 | /** |
同步
同步加载:LoadingCache 对象进行缓存的操作,使用 CacheLoader 进行缓存存储管理。批量查找可以使用 getAll () 方法。默认情况下,getAll () 将会对缓存中没有值的 key 分别调用 CacheLoader.load 方法来构建缓存的值(build 中的表达式)。我们可以重写 CacheLoader.loadAll 方法来提高 getAll () 的效率。
1 | /** |
异步
自动异步加载:
AsyncLoadingCache 对象进行缓存管理,get () 返回一个 CompletableFuture 对象,默认使用 ForkJoinPool.commonPool () 来执行异步线程,但是我们可以通过 Caffeine.executor (Executor) 方法来替换线程池。
1 | /** |
手动异步加载:
1 | /** |
过期策略
Caffeine 的缓存清除是惰性的,可能发生在读请求后或者写请求后,比如说有一条数据过期后,不会立即删除,可能在下一次读 / 写操作后触发删除(类比于 redis 的惰性删除)。如果读请求和写请求比较少,但想要尽快的删掉 cache 中过期的数据的话,可以通过增加定时器的方法,定时执行 cache.cleanUp () 方法(异步方法,可以等待执行),触发缓存清除操作。
基于大小过期:当缓存超出后,使用 W-TinyLFU 算法进行缓存淘汰处理
- 基于缓存条目过期
maximumSize () 方法,参数是缓存中存储的最大缓存条目,当添加缓存时达到条目阈值后,将进行缓存淘汰操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* 淘汰策略-size
*/
public void sizeTest() {
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumSize(1)
.build(this::buildLoader);
List<String> list = Lists.newArrayList("c1", "c2", "c3");
loadingCache.put(list.get(0), list.get(0));
log.info("weightTest get: [{}] -> [{}]", list.get(0), loadingCache.get(list.get(0)));
loadingCache.put(list.get(1), list.get(1));
log.info("weightTest get: [{}] -> [{}]", list.get(1), loadingCache.get(list.get(1)));
loadingCache.put(list.get(2), list.get(2));
log.info("weightTest get: [{}] -> [{}]", list.get(2), loadingCache.get(list.get(2)));
log.info("weightTest cache map:{}", loadingCache.getAll(list));
}- 基于缓存条目过期
基于权重过期:权重大小不会决定缓存满时清楚的优先级
weigher () 方法可以指定缓存所占比重,maximumWeight () 方法指定最大的权重阈值,当添加缓存超过规定权重后,进行数据淘汰1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/**
* 淘汰策略-weight
*/
public void weightTest() {
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumWeight(10)
.weigher((key, value) -> 5)
.build(this::buildLoader);
List<String> list = Lists.newArrayList("c1", "c2", "c3");
loadingCache.put(list.get(0), list.get(0));
log.info("weightTest get: [{}] -> [{}]", list.get(0), loadingCache.get(list.get(0)));
loadingCache.put(list.get(1), list.get(1));
log.info("weightTest get: [{}] -> [{}]", list.get(1), loadingCache.get(list.get(1)));
log.info("weightTest cache map:{}", loadingCache.getAll(list));
}
基于时间过期
expireAfterAccess ():缓存访问后,一定时间失效;即最后一次访问或者写入开始时计时。
expireAfterWrite ():缓存写入后,一定时间失效;以写入缓存操作为准计时。
expireAfter ():自定义缓存策略,满足多样化的过期时间要求。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33/**
* 淘汰策略-time
*/
public void timeTest() {
//1.缓存访问后,一定时间后失效
LoadingCache<String, String> loadingCacheOne = Caffeine.newBuilder()
.expireAfterAccess(10L, TimeUnit.SECONDS)
.build(this::buildLoader);
//2.缓存写入后,一定时间后失效
LoadingCache<String, String> loadingCacheTwo = Caffeine.newBuilder()
.expireAfterWrite(10L, TimeUnit.SECONDS)
.build(this::buildLoader);
//3.自定义过期策略
LoadingCache<String, String> loadingCacheThree = Caffeine.newBuilder()
.expireAfter(new Expiry<Object, Object>() {
public long expireAfterCreate(long l) Object o, Object o2, {
return 0;
}
public long expireAfterUpdate(long l, long l1) Object o, Object o2, {
return 0;
}
public long expireAfterRead(long l, long l1) Object o, Object o2, {
return 0;
}
})
.build(this::buildLoader);
}基于引用过期
引用名称 垃圾回收时机 用途 生存周期
强引用(Strong Reference) 不回收 正常普遍的类别 JVM 停止运行
软引用(soft Reference) 内存不足回收 缓存对象 内存不足时
弱引用(weak Reference) 下一次 GC 回收 缓存对象 GC 之后
虚引用(Phantom Reference) 随时回收,引用虚设 通知功能 随时回收1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* 淘汰策略-引用
*/
public void referenceTest() {
//1.弱引用弱key方式,没有强引用时,回收
LoadingCache<String, String> loadingCacheOne = Caffeine.newBuilder()
.weakKeys()
.weakValues()
.build(this::buildLoader);
//2.软值引用,内存不足的时候回收
LoadingCache<String, String> loadingCacheTwo = Caffeine.newBuilder()
.softValues()
.build(this::buildLoader);
} Caffeine.weakKeys ():使用弱引用存储 key,当没有其他的强引用时,则会被垃圾回收器回收;
Caffeine.weakValues ():使用弱引用存储 value,当没有其他的强引用时,则会被垃圾回收器回收;
Caffeine.softValues ():使用软引用存储 key,当没有其他的强引用时,内存不足的时候会被回收;
注:Weak or soft values can not be combined with AsyncCache
手动删除
Caffeine 中提供了手动进行缓存的删除,无需等待我们上面提到的被动的一些删除策略,使用方法如下:
1 | cacheOne.invalidateAll(); |
事件监听
移除事件 RemovalListener 是一种缓存监听事件,当 key 被移除的时候就会触发这个方法,可以进行一些相关联的操作。RemovalListener 可以获取到 key、value 和 RemovalCause(删除的原因)。另外 RemovalListener 中操作是线程池异步执行的。
1 | /** |
参数总结
1 | maximumSize:设置缓存最大条目数,超过条目则触发回收 |
Springboot 集成
spring 从 3 的版本开始支持 Cache 的处理,使用方式也是按照传统方向提供两种,基于注解和基于 xml 配置文件的方式,其操作纬度为方法或类,比如将注解 @Cacheable 按要求配置到一个方法上,会构造出一个 k-v 的缓存信息,k 是入参相关,value 就是方法的返回值,当请求到这个方法上,先去缓存获取,有则直接返回,无则执行流程然后返回且存入缓存。还有一些其他的注解,例如:@CachePut、@CacheEvict 等。
pom引入
1 | <dependency> |
yml 文件配置
1 | spring: |
启动类加注解:@EnableCaching
1 |
|
主要注解
1 | @Cacheable 触发缓存入口(这里一般放在创建和获取的方法上) |
案例
1 |
|