高性能本地缓存-caffeine
简介
1、Caffeine 是基于 JAVA 8 的高性能缓存库。并且在 spring5 (springboot 2.x) 后,spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件
2、支持异步加载,事件提交队列
3、内部使用W-TinyLFU算法,它的命中率非常高,内存占用更加的小
4、一般在redis之后,做二级缓存
优势
性能比较:
8个线程同时从缓存中读取
8个线程同时从缓存中写入
6个线程读取,2个线程写入
使用场景
1、二级缓存
原生使用方式
### 填充策略
1、手动加载
// 初始化
public static void main(String[] args) {
Cache cache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.expireAfterAccess(1, TimeUnit.SECONDS)
.maximumSize(10)
.build();
String key = "testKey";
//如果一个key不存在,那么会进入指定的函数生成value
Object value = cache.get(key, t -> setValue(key).apply(key));
cache.put("hello",value);
//判断是否存在如果不存返回null
Object ifPresent = cache.getIfPresent(key);
System.out.println(ifPresent);
//移除一个key
cache.invalidate(key);
}
public static Function setValue(String key){
return t -> key + "value";
}
2、同步加载
构造Cache时候,build方法传入一个CacheLoader实现类。实现load方法,通过key加载value
public static void main(String[] args) {
String key = "key";
LoadingCache cache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(1, TimeUnit.MINUTES)
.build(k -> setValue(key).apply(key));
System.out.println(cache.get(key));
}
public static Function setValue(String key){
return t -> key + "value";
}
3、异步加载
AsyncLoadingCache是继承自LoadingCache类的,异步加载使用Executor去调用方法并返回一个CompletableFuture。异步加载缓存使用了响应式编程模型
public static void main(String[] args) {
String key = "key";
AsyncLoadingCache cache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(1, TimeUnit.MINUTES)
.buildAsync(k -> setValue(key).get());
// 异步获取
CompletableFuture
springboot集成springboot-cache使用
1、添加jar依赖
org.springframework.boot
spring-boot-starter-cache
com.github.ben-manes.caffeine
caffeine
2、配置@EnableCaching注解
@EnableCaching
@Configuration
public class LocalCacheConfig {
@Bean("caffeineCacheManager")
public CacheManager cacheManager(){
CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
caffeineCacheManager.setCaffeine(Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).initialCapacity(100).maximumSize(5000));
return caffeineCacheManager;
}
}
3、使用
@Slf4j
@Service
@CacheConfig(cacheNames = "caffeineCacheManager")
public class SafetyInspectionTrackServiceImpl implements SafetyInspectionTrackService {
@Autowired
private SafetyInspectionTrackMapper safetyInspectionTrackMapper;
@Override
@Cacheable(key = "#id")
public SafetyInspectionTrackDO getById(Long id) {
log.info("进入缓存:{}",id);
return safetyInspectionTrackMapper.selectById(id);
}
@Override
@CacheEvict
public void clear(Long id){
log.info("清除缓存");
}
}
@Cacheable:
配置在 getUsersByName方法上表示其返回值将被加入缓存。同时在查询时,会先从缓存中
获取,若不存在才再发起对数据库的访问
@CachePut:
配置于方法上时,能够根据参数定义条件来进行缓存,其与 @Cacheable不同的是使用
@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都
会执行该方法并将执行结果以键值对的形式存入指定的缓存中,所以主要用于数据新增和修改
操作上
@CacheEvict:
配置于方法上时,表示从缓存中移除相应数据
Caffeine常用配置说明
1、初始化参数
initialCapacity: 初始的缓存空间大小
maximumSize: 缓存的最大条数
maximumWeight: 缓存的最大权重
expireAfterAccess: 最后一次写入或访问后经过固定时间过期
expireAfterWrite: 最后一次写入后经过固定时间过期
refreshAfterWrite: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存
weakKeys: 打开key的弱引用
weakValues:打开value的弱引用
softValues:打开value的软引用
recordStats:开发统计功能
注意:
expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。
maximumSize和maximumWeight不可以同时使用
weakValues和softValues不可以同时使用
2、自动刷新
refreshAfterWrite就是设置写入后多就会刷新,expireAfterWrite和refreshAfterWrite
的区别是,当缓存过期后,配置了expireAfterWrite,则调用时会阻塞,等待缓存计算完成,返
回新的值并进行缓存,refreshAfterWrite则是返回一个旧值,并异步计算新值并缓存
3、回收策略
#### (1)基于大小
maximumSize 初始给定缓存大小,超过设置的值后,自动回收
#### (2)基于时间
expireAfterAccess:在最后一次访问或者写入后开始计时
expireAfterWrite:在最后一次写入缓存后开始计时
expireAfte:自定义策略
#### (3)基于引用
weakKeys:使用弱引用存储key
weakValues:使用弱引用存储value
softValues:使用软引用存储value
软引用: 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
弱引用: 弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
淘汰策略算法
1、FIFO:先进先出
在这种淘汰算法中,先进入缓存的会先被淘汰,会导致命中率很低。
2、LRU:最近最少使用算法
每次访问数据都会将其放在我们的队尾,如果需要淘汰数据,就只需要淘汰队首即可。仍然有个问题,如果有个数据在 1 分钟访问了 1000次,再后 1 分钟没有访问这个数据,但是有其他的数据访问,就导致了我们这个热点数据被淘汰。
3、LFU:最近最少频率使用
利用额外的空间记录每个数据的使用频率,然后选出频率最低进行淘汰。这样就避免了 LRU 不能处理时间段的问题。
4、W-tinyLFU算法
详细介绍 https://www.cnblogs.com/liuji...
源码简析
1、在Caffeine中有个LocalCacheFactory类,他会根据你的配置进行具体Cache的创建。
2、在Caffeine中有个scheduleDrainBuffers方法,用来进行我们的过期任务的调度
常见问题(Faq)
#### 1、固定数据(Pinning Entries)
固定数据是不能通过驱逐策略去将数据删除的。当数据是一个有状态的资源时(如锁)