本地缓存Caffeine Cache
DB + Redis + LocalCache = 高效存储,高效访问
Caffeine简介
Caffeine是基于Java 8的高性能,接近最佳的缓存工具库。Caffeine使用Google Guava启发的API提供内存缓存。所以它的使用成本较低,跟Guava的API基本一致。
它主要有以下几个功能:
- 自动将条目加载到缓存中,可以选择同步或异步加载
- 基于频率和新近度超过最大值时基于大小的逐出
- 自上次访问或上次写入以来测得的基于时间的条目到期
- 发生第一个陈旧的条目请求时,异步刷新
- 键自动包装在弱引用中
- 值自动包装在弱引用或软引用中
- 逐出(或以其他方式删除)条目的通知
- 写入通知
- 缓存访问统计信息的
快速入门
Cache分为LoadingCache(同步缓存),AsyncLoadingCache(异步缓存)。
pom 依赖
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.5</version>
</dependency>
创建对象
Cache<String, Object> cache = Caffeine.newBuilder()
.initialCapacity(100)//初始大小
.maximumSize(200)//最大数量
.expireAfterWrite(3, TimeUnit.SECONDS)//过期时间
.build();
参数介绍:
- initialCapacity:初始的缓存空间大小
- maximumSize:缓存的最大数量
- maximumWeight:缓存的最大权重
- expireAfterAccess:最后一次读或写操作后经过指定时间过期
- expireAfterWrite:最后一次写操作后经过指定时间过期
- refreshAfterWrite:创建缓存或者最近一次更新缓存后经过指定时间间隔,刷新缓存
- weakKeys:打开key的弱引用
- weakValues:打开value的弱引用
- softValues:打开value的软引用
- recordStats:开发统计功能
注意:
- expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。
- maximumSize和maximumWeight不可以同时使用。
添加数据
Caffeine 为我们提供了手动、同步和异步这几种填充策略。
Cache<String, String> cache = Caffeine.newBuilder()
.build();
cache.put("java金融", "java金融");
System.out.println(cache.getIfPresent("java金融"));
自动添加(自定义添加函数)
public static void main(String[] args) {
Cache<String, String> cache = Caffeine.newBuilder()
.build();
// 1.如果缓存中能查到,则直接返回
// 2.如果查不到,则从我们自定义的getValue方法获取数据,并加入到缓存中
String val = cache.get("java金融", k -> getValue(k));
System.out.println(val);
}
/**
* 缓存中找不到,则会进入这个方法。一般是从数据库获取内容
*
* @param k
* @return
*/
private static String getValue(String k) {
return k + ":value";
}
过期策略
Caffeine 为我们提供了三种过期策略,分别是基于大小(size-based)、基于时间(time-based)、基于引用(reference-based)
基于大小(size-based)
public static void main(String[] args) {
LoadingCache<String, String> cache = Caffeine.newBuilder()
// 最大容量为1
.maximumSize(1)
.build(k -> getValue(k));
cache.put("java金融1", "java金融1");
cache.put("java金融2", "java金融2");
cache.put("java金融3", "java金融3");
cache.cleanUp();
System.out.println(cache.getIfPresent("java金融1"));
System.out.println(cache.getIfPresent("java金融2"));
System.out.println(cache.getIfPresent("java金融3"));
}
/**
* * 缓存中找不到,则会进入这个方法。一般是从数据库获取内容
* * @param k
* * @return
*/
private static String getValue(String k) {
return k + ":value";
}
运行结果如下:淘汰了两个只剩下一个。
null
null
java金融3
基于时间(time-based)
Caffeine提供了三种定时驱逐策略:
expireAfterWrite(long, TimeUnit)
在最后一次写入缓存后开始计时,在指定的时间后过期。
public static void main(String[] args) throws Exception{
LoadingCache<String, String> cache = Caffeine.newBuilder()
// 最大容量为1
.maximumSize(1)
.expireAfterWrite(3, TimeUnit.SECONDS)
.build(k->getValue(k));
cache.put("java金融","java金融");
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("java金融"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("java金融"));
Thread.sleep(1*1000);
System.out.println(cache.getIfPresent("java金融"));
}
/**
* * 缓存中找不到,则会进入这个方法。一般是从数据库获取内容
* * @param k
* * @return
*/
private static String getValue(String k) {
return k + ":value";
}
运行结果第三秒的时候取值为空:
java金融
java金融
null
expireAfterAccess
在最后一次读或者写入后开始计时,在指定的时间后过期。假如一直有请求访问该key,那么这个缓存将一直不会过期。
public static void main(String[] args) throws Exception {
LoadingCache<String, String> cache = Caffeine.newBuilder()
// 最大容量为1
.maximumSize(1)
.expireAfterAccess(3, TimeUnit.SECONDS)
.build(k -> getValue(k));
cache.put("java金融", "java金融");
Thread.sleep(1 * 1000);
System.out.println(cache.getIfPresent("java金融"));
Thread.sleep(1 * 1000);
System.out.println(cache.getIfPresent("java金融"));
Thread.sleep(1 * 1000);
System.out.println(cache.getIfPresent("java金融"));
Thread.sleep(3001);
System.out.println(cache.getIfPresent("java金融"));
}
/**
* * 缓存中找不到,则会进入这个方法。一般是从数据库获取内容
* * @param k
* * @return
*/
private static String getValue(String k) {
return k + ":value";
}
运行结果:读和写都没有的情况下,3秒后才过期,然后就输出了null。
java金融
java金融
java金融
null
expireAfter(Expiry)
在expireAfter中需要自己实现Expiry接口,这个接口支持expireAfterCreate,expireAfterUpdate,以及expireAfterRead了之后多久过期。注意这个是和expireAfterAccess、expireAfterAccess是互斥的。这里和expireAfterAccess、expireAfterAccess不同的是,需要你告诉缓存框架,它应该在具体的某个时间过期,获取具体的过期时间。
public static void main(String[] args) throws Exception {
LoadingCache<String, String> cache = Caffeine.newBuilder()
// 最大容量为1
.maximumSize(1)
.removalListener((key, value, cause) ->
System.out.println("key:" + key + ",value:" + value + ",删除原因:" + cause))
.expireAfter(new Expiry<String, String>() {
@Override
public long expireAfterCreate(@NonNull String key, @NonNull String value, long currentTime) {
return currentTime;
}
@Override
public long expireAfterUpdate(@NonNull String key, @NonNull String value, long currentTime, @NonNegative long currentDuration) {
return currentTime;
}
@Override
public long expireAfterRead(@NonNull String key, @NonNull String value, long currentTime, @NonNegative long currentDuration) {
return currentTime;
}
})
.build(k -> getValue(k));
}
/**
* * 缓存中找不到,则会进入这个方法。一般是从数据库获取内容
* * @param k
* * @return
*/
private static String getValue(String k) {
return k + ":value";
}
删除
- 单个删除:Cache.invalidate(key)
- 批量删除:Cache.invalidateAll(keys)
- 删除所有缓存项:Cache.invalidateAll
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)