Caffeine cache实现本地缓存题
- 缓存填充策略
- 手动加载
-
- 同步加载
-
- 异步加载
- 介绍:
- 注意: 异步和同步使用方式相似, 这里的话主要写一下创建和异步转同步
- 使用方式:
- 过期策略
- 基于大小过期
- 介绍:当缓存超出后,使用W-TinyLFU算法进行缓存淘汰处理
- 使用方式:
- 基于权重过期
-
- 基于时间过期
-
- 基于引用回收
-
- 基本使用
- 手动删除
-
- 自动刷新
-
- 移除通知
-
- 外部存储
-
- 统计缓存使用情况
-
- 实例整合
- 编码方式
-
- 注解方式(整合SpringBoot的cache)
- 介绍:
- 使用方式:
- @Cacheable注解:
- @CachePut注解:
- @CacheEvict注解:
- @CacheConfig注解:
- @Caching注解:
- 简易缓存工具类
缓存填充策略
介绍:缓存的填充方式有三种,手动、同步和异步
手动加载
介绍:
手动控制缓存的增删改处理,主动增加、获取以及依据函数式更新缓存,底层使用ConcurrentHashMap进行节点存储,因此get方法是安全的。批量查找可以使getAllPresent()方法或者带填充默认值的getAll()方法
使用方式:
@Test
void test1() {
com.github.benmanes.caffeine.cache.@NonNull Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(1, TimeUnit.DAYS)
.build();
cache.put("cl", "cl");
System.out.println(cache.getIfPresent("cl"));
System.out.println(cache.get("abc", this::buildLoader));
cache.invalidate("abc");
System.out.println(cache.getIfPresent("abc"));
}
String buildLoader(String k) {
return k + "+default";
}
同步加载
介绍:
LoadingCache对象进行缓存的操作,使用CacheLoader进行缓存存储管理。 批量查找可以使用getAll()方法。 默认情况下, getAll()将会对缓存中没有值的key分别调用CacheLoader.load方法来构建缓存的值(build中的表达式)。我们可以重写CacheLoader.loadAll方法来提高getAll()的效率。
使用方式:
@Test
void test1() {
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(1, TimeUnit.DAYS)
.build(this :: buildLoader);
List<String> keys = new ArrayList<>();
keys.add("cl");
keys.add("b");
keys.add("c");
keys.add("d");
Map<String, String> cacheAll = loadingCache.getAll(keys);
System.out.println(cacheAll.get("b"));
System.out.println(cacheAll.get("c"));
System.out.println(cacheAll.get("a"));
}
String buildLoader(String k) {
return k + "+default";
}
异步加载
介绍:
AsyncLoadingCache对象进行缓存管理,get()返回一个CompletableFuture对象,默认使用ForkJoinPool.commonPool()来执行异步线程,但是我们可以通过Caffeine.executor(Executor) 方法来替换线程池
注意: 异步和同步使用方式相似, 这里的话主要写一下创建和异步转同步
使用方式:
@Test
void test1() {
AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(1,TimeUnit.DAYS)
.buildAsync(k-> this.buildLoaderAsync(k).get());
try {
System.out.println(asyncLoadingCache.get("123").get());
} catch (Exception e) {
e.printStackTrace();
}
}
CompletableFuture<String> buildLoaderAsync(String k) {
return CompletableFuture.supplyAsync(() -> k + "buildLoaderAsync");
}
转换使用方式:
AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(1,TimeUnit.DAYS)
.buildAsync(k -> this.buildLoaderAsync(k).get());
LoadingCache<String, String> cache = asyncLoadingCache.synchronous();
System.out.println("------------");
System.out.println(cache.get("dd"));
过期策略
介绍:
Caffeine的缓存清除是惰性的,可能发生在读请求后或者写请求后
比如说有一条数据过期后,不会立即删除,可能在下一次读/写操作后触发删除(类比于redis的惰性删除)。
如果读请求和写请求比较少,但想要尽快的删掉cache中过期的数据的话,
可以通过增加定时器的方法,定时执行cache.cleanUp()方法(异步方法,可以等待执行),触发缓存清除操作。
基于大小过期
介绍:当缓存超出后,使用W-TinyLFU算法进行缓存淘汰处理
使用方式:
maximumSize()方法,参数是缓存中存储的最大缓存条目,当添加缓存时达到条目阈值后,将进行缓存淘汰操作
AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
.maximumSize(100)
基于权重过期
介绍:
通过权重来计算,每个实体都有不同的权重,总权重到达最高时淘汰实体。
weigher()方法可以指定缓存所占比重,maximumWeight()方法指定最大的权重阈值,当添加缓存超过规定权重后,进行数据淘汰
使用方式:
com.github.benmanes.caffeine.cache.@NonNull LoadingCache<String, String> cache = Caffeine.newBuilder()
.maximumWeight(10)
.weigher(new Weigher<Object, Object>() {
@Override
public @NonNegative int weigh(@NonNull Object o, @NonNull Object o2) {
System.out.println("key" + o + "value" + o2);
return 5;
}
})
.build(this::buildLoader);
List<String> list = Lists.newArrayList("c1", "c2", "c3");
cache.put(list.get(0), list.get(0));
System.out.println(list.get(0) + "--" + cache.get(list.get(0)));
cache.put(list.get(1), list.get(1));
System.out.println(list.get(1) + "---" + cache.get(list.get(1)));
System.out.println(cache.getAll(list));
基于时间过期
介绍:
expireAfterAccess():缓存访问后,一定时间失效;即最后一次访问或者写入开始时计时。
expireAfterWrite():缓存写入后,一定时间失效;以写入缓存操作为准计时。(在最后一次写入缓存后开始计时,在指定的时间后过期。)
expireAfter():自定义缓存策略,满足多样化的过期时间要求。
这里只展示after, 其他的话 使用方式大致相同,自己动手试试
注意:当expireAfterAccess和expireAfterWrite同时存在时,只有expireAfterWrite有效
使用方式:
com.github.benmanes.caffeine.cache.@NonNull LoadingCache<String, String> cache = Caffeine.newBuilder()
.expireAfter(new Expiry<Object, Object>() {
@Override
public long expireAfterCreate(@NonNull Object o, @NonNull Object o2, long l) {
return 1;
}
@Override
public long expireAfterUpdate(@NonNull Object o, @NonNull Object o2, long l, @NonNegative long l1) {
return 1;
}
@Override
public long expireAfterRead(@NonNull Object o, @NonNull Object o2, long l, @NonNegative long l1) {
return 1;
}
}).build(this::buildLoader);
基于引用回收
介绍:
使用方式:
基本使用
手动删除
使用方式:
自动刷新
使用方式:
refreshAfterWrite:这里设置的是1分钟后自动刷新
移除通知
使用方式:
外部存储
使用方式:
统计缓存使用情况
使用方式:
实例整合
编码方式
使用方式:
1、缓存配置类
@Configuration
public class CacheConfig {
@Bean
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
.expireAfterWrite(60, TimeUnit.SECONDS)
.initialCapacity(100)
.maximumSize(1000)
.build();
}
}
2、使用
注意:这里我只写了存入的写法, 如果要获取缓存,用cache的get缓存我们存储的key就可以了。
如:
cache.put(1,"测试缓存")
cache.get("1");
cache.get("1", value -> value);
cache.asMap().get("1");
示例:
private final Cache<Object, Object> cache;
public FeedbackServiceImpl( Cache<Object, Object> cache) {
this.cache = cache;
}
public void insertFeedBack(Feedback feedback) {
int i = feedbackMapper.insert(feedback);
cache.put(feedback.getId(), feedback);
}
注意:如果你只是简单的使用, 你可以使用末文的缓存工具类
注解方式(整合SpringBoot的cache)
介绍:
使用方式:
@Cacheable注解:
@CachePut注解:
@CacheEvict注解:
@CacheConfig注解:
@CacheConfig(cacheNames="emp")
@Caching注解:
示例:
@EnableCaching
public class Springboot01CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01CacheApplication.class, args);
}
}
@Cacheable(cacheNames = {"emp"},condition = "#id>0")
public Employee getEmp(Integer id){
System.out.println("查询"+id+"号员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
简易缓存工具类
具体含义看代码注释
/**
* @author huangye 所谓致知在格物者,言欲致吾之知,在即物而穷其理也。
* @version 1.0 2020/10/4
*/
public class CacheUtil{
private static Cache<String, Object> cache;
static {
cache = createCache();
}
/**
* 插入缓存 | 更新缓存
* @param cacheName 缓存前缀
* @param key 键
* @param value 值
*/
public static void saveCache(String cacheName, String key, Object value) {
System.out.printf("存储缓存的数据 key(%s) value(%s)",cacheName + "-" + key, value);
System.out.println();
cache.asMap().put(cacheName + "-" + key, value);
}
/**
* 根据键获取缓存
* @param cacheName 缓存前缀
* @param key 键
* @return Object类型, 由使用者自己转换
*/
public static Object getCache(String cacheName, String key) {
System.out.printf("获取缓存的数据 key(%s) value(%s)",cacheName + "-" + key,
cache.asMap().get(cacheName + "-" + key) );
System.out.println();
return cache.asMap().get(cacheName + "-" + key);
}
/**
* 根据键值删除缓存
* @param cacheName 缓存前缀
* @param key 建
*/
public static void deleteCache(String cacheName, String key) {
System.out.printf("删除缓存的数据 key(%s)", cacheName + "-" + key);
System.out.println();
cache.asMap().remove(cacheName + "-" + key);
}
private static class CacheSingletonHolder {
private final static Cache<String, Object> CACHE = Caffeine.newBuilder()
// 初始的缓存空间大小
.initialCapacity(100)
// 缓存的最大条数
.maximumSize(1000)
// 最后一次缓存访问过后,7天后失效
.expireAfterAccess(7, TimeUnit.DAYS)
.build();
}
private static Cache<String, Object> createCache() {
return CacheSingletonHolder.CACHE;
}
}
ps:这里的图片都来源于我学习时做的笔记(下图位证), 因为有的是在网上找文章学习的,所以可能会遇到有的是和别人一样的内容,我是想着吧那些之前看的文章放到文章末尾的,但是我找不到了非常抱歉, 我这里的话只是想做个知识的汇总,如果有错误的地方,可以评论留言 我会改正,谢谢
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)