最近代码里看到有部分关于guava写本地缓存的,学习记录一下。
首先guava的本地缓存可以理解成一个缓存map,以kv的形式存数据,不会持久化,没有支持分布式。比redis使用起来方便,不用引入额外的组件。如果是单机缓存的话,可以首先选择使用这种缓存方式。
Guava cache的设计来源于CurrentHashMap,是线程安全的,可以按照多种策略来清理存储在其中的缓存值且保持很高的并发读写性能。常见应用场景:对性能有非常高的要求、不经常变化、占用内存不大、有访问整个集合的需求、数据允许不时时一致。
Guava cache的优点
多种清理和淘汰策略
支持并发(采用Segment做分区,线程安全)
更新锁定(对同一个key,只让一个请求去读源并回填缓存,其他请求阻塞等待)
集成数据源(一般我们在业务中操作缓存,都会操作缓存和数据源两部分GuavaCache的get可以集成数据源,在从缓存中读取不到时可以从数据源中读取数据并回填缓存)
使用方法
引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
抽象类
//引入数据库DAO
//定义缓存的map格式
public LoadingCache<Long,String> testCache
//初始化
@PostConstruct
private void init(){
testCache = CacheBuilder.newBuilder()
// 初始大小
.initialCapacity(1000)
// 缓存池大小
.maximumSize(1000)
// 设置时间对象没有被读/写访问则对象从内存中删除
.expireAfterWrite(100, TimeUnit.MINUTES)
//设置时间刷新缓存
.refreshAfterWrite(60,TimeUnit.SECONDS)
// 移除监听器
.removalListener(
new RemovalListener<Long, String>() {
@Override
public void onRemoval(RemovalNotification<Long, String> rn) {
handleRemove.accept(rn.getKey());
}
})
.recordStats()
.build(
new CacheLoader<Long, String>() {
//第一次加载
@Override
public String load(Long aLong) throws Exception {
return handleNotExist.apply(aLong);
}
//异步刷新(过期刷新机制,调用LoadingCache.refresh优先调用这里,未重写则调上面load)
@Override
public ListenableFuture<String> reload(Long aLong, String oldValue) throws Exception {
ListenableFuture<String> task = ListenableFutureTask.creat( new Callable<String>(){
public String call(){
//请求数据库
return querySqlDAO(key);
}
});
refreshTaskExecutor.execute(task);
return task;
}
}
log.info("初始化缓存");
);
实现类,直接继承抽象类后,写对应的query和refresh函数,参数判断,异常处理等。调用定义缓存LoadingCache的get/refresh方法,其中入参都是缓存的key,刷新会优先调用重写的reload去更新缓存,一般都重写为异步加载。如果reload没有被重写,则调用load走第一次加载的逻辑。
使用的时候直接调用实现类中对应缓存的方法即可。
参考:
https://blog.csdn.net/ABestRookie/article/details/119901114