一、简介
Caffeine是基于Java 8的高性能,接近最佳的缓存工具库。Caffeine使用Google Guava启发的API提供内存缓存。所以它的使用成本较低,跟Guava的API大致一致。
它主要有以下几个功能:
二、常见API
Cache分为LoadingCache(同步缓存),AsyncLoadingCache(异步缓存)。
- pom 依赖
<dependency>
<groupId>com.github.benmanes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.8.5</version>
</dependency>
2.1 人工加载策略
Cache<Object, Object> cache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS)
.expireAfterAccess(1, TimeUnit.SECONDS)
.maximumSize(10)//最大条数
.build();//定义cache
User user1=(User) cache.get(id, v-> userDao.getOne(id));//如果cache不存在,查询数据库
2.2 自动加载同步
LoadingCache<String, User> userDaoCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.SECONDS)
.expireAfterAccess(1, TimeUnit.SECONDS).maximumSize(10).build(key -> userDao.getOne(key))
User user = userDaoCache.get("1");
2.3 自动加载异步
AsyncLoadingCache<String, User> asynUserDaoCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.SECONDS).expireAfterAccess(1, TimeUnit.SECONDS).maximumSize(10)
.buildAsync(key -> userDao.getOne(key));
User user = asynUserDaoCache.get("1").get();
三、其他API
3.1 统计缓存信息
Cache<Object, Object> cacheStats = Caffeine.newBuilder()
.expireAfterWrite(1000, TimeUnit.SECONDS)
.expireAfterAccess(1000, TimeUnit.SECONDS)
.recordStats()//记录统计信息
.weakKeys()//key弱引用
.weakValues()//value 弱引用
.maximumSize(10).build()
CacheStats stats = cacheStats.stats();//获取统计信息
3.2 CacheWriter
LoadingCache<String, User> cacheWriter = Caffeine.newBuilder().expireAfterAccess(3, TimeUnit.SECONDS)
.writer(new CacheWriter<String, User>() {
@Override
public void write(String id, User user) {
System.out.println("***写入***" + id);//当混存数据时,调用此方案
}
@Override
public void delete(String id, User user, RemovalCause cause) {
System.out.println("***delete***" + id);//当手动删除数据时,调用此方法
}
}).build(id -> userDao.getOne(id));
3.3RemovalListener监听(手动删除)
LoadingCache<String, User> removalListener = Caffeine.newBuilder()
.removalListener(new RemovalListener<String, User>() {
@Override
public void onRemoval(@Nullable String key, @Nullable User value, @NonNull RemovalCause cause) {
System.out.println(key + "*****" + value);
}
}).expireAfterAccess(3, TimeUnit.SECONDS).build(id -> userDao.getOne(id));
四、缓存淘汰机制
常见缓存机制有:LRU(Least Recently Used)最近最少使用跟LFU(Least Frequently Used)最不经常使用。
4.1 LRU
- LRU:如果一个数据在最近一段时间没有被访问到,那么认为在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰
- 缺点:注重对数据的使用时间上是不是最近。缓存数目超过容量,缓存数据很容易被淘汰。
4.2 LFU
- LFU:如果一个数据在最近一段时间很少被访问到,那么可以认为在将来它被访问的可能性也很小。因此,当空间满时,最小频率访问的数据最先被淘汰
- 缺点:注重对元素的使用频次上是不是较大。
1.在短时间内如果某些元素访问频率很高,会导致后续很难淘汰,造成无效数据缓存。
2.突发流量的时候,可能由于没有及时达到足够的频率数据来保证自己驻留在缓存中,从而导致缓存的命中率下降
4.3 W-TinyLFU
为了弥补LRU跟LFU的不足与缺点,产生了W-TinyLFU算法。
W-TinyLFU算法大致思想:缓存有窗口缓存跟主缓存两部门组成。窗口缓存没有淘汰策略,从而解决了突发流量时不达标不缓存的问题。而TinyLFU维护了近期访问记录的频率信息,作为一个过滤器,当新记录来时,只有满足TinyLFU要求的记录才可以被插入缓存,也因此解决了缓存超过容量轻易淘汰问题。
caffeine采用的就是这种策略。对算法的具体过程感兴趣,可以自行网上找资料了解下。
五、参数总结
maximumSize:设置缓存最大条目数,超过条目则触发回收
maximumWeight:设置缓存最大权重,设置权重是通过weigher方法, 需要注意的是权重也是限制缓存大小的参数,并不会影响缓存淘汰策略,也不能和maximumSize方法一起使用。
weakKeys:将key设置为弱引用,在GC时可以直接淘汰
weakValues:将value设置为弱引用,在GC时可以直接淘汰
softValues:将value设置为软引用,在内存溢出前可以直接淘汰
expireAfterWrite:写入后隔段时间过期
expireAfterAccess:访问后隔断时间过期
refreshAfterWrite:写入后隔断时间刷新
removalListener:缓存淘汰监听器,配置监听器后,每个条目淘汰时都会调用该监听器
writer:writer监听器其实提供了两个监听,一个是缓存写入或更新是的write,一个是缓存淘汰时的delete,每个条目淘汰时都会调用该监听器
网友评论