caffeine-cache - 按大小驱逐咖啡因似乎不起作用
问题描述
我正在使用咖啡因缓存。
我想把它放在大小限制之下,但它不能正常工作。
测试1:
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(3)
.build();
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(3)
.build();
for (int i = 1; i <= 10; i ++) {
String val = String.valueOf(i);
cache.put(val, val);
}
System.out.println("cache size: " + cache.estimatedSize() + ", cache keys: " + cache.asMap().values().stream().collect(Collectors.joining(",")));
result: cache size: 10, cache keys: 1,2,10
另一个测试:尝试获取密钥并将最大值设置为 1
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(1)
.build();
for (int i = 1; i <= 10; i ++) {
String val = String.valueOf(i);
cache.put(val, val);
if (i % 2 == 0) {
cache.getIfPresent("5");
}
}
System.out.println("cache size: " + cache.estimatedSize() + ", cache keys: " + cache.asMap().values().stream().collect(Collectors.joining(",")));
cache size: 10, cache keys: 2,3,4,5,6,7,8,9,10
最后一次测试:运行 100 次,最大尺寸 1
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(1)
.build();
for (int i = 1; i <= 100; i ++) {
String val = String.valueOf(i);
cache.put(val, val);
if (i % 2 == 0) {
cache.getIfPresent("5");
}
}
System.out.println("cache size: " + cache.estimatedSize() + ", cache keys: " + cache.asMap().values().stream().collect(Collectors.joining(",")));
cache size: 99, cache keys: 96,97,99,19,23,58
有人可以帮我理解这一点以及如何使其正常工作吗?
感谢 Ben Manes,我补充说.executor(Runnable::run)
现在这样做之后,我只得到 3 件物品
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(3)
.executor(Runnable::run)
.build();
for (int i = 1; i <= 10; i ++) {
String val = String.valueOf(i);
cache.put(val, val);
if (i % 2 == 0) {
cache.getIfPresent("5");
}
}
cache.cleanUp();
System.out.println("cache size: " + cache.estimatedSize() + ", cache: " + CodecUtils.toJson(cache.asMap().values()));
cache size: 3, cache: ["3","9","10"]
- 这不会阻止我的线程吗?
- 为什么我已经多次使用它的缓存中没有键 5?
解决方案
默认情况下,缓存将异步执行一些操作,例如驱逐和通知删除侦听器。这是为了最小化请求延迟,因为请求本身不需要辅助工作,并且用户提供的回调可能很昂贵。
缓存自身的维护工作非常便宜,因此如果需要,您可以使用Caffeine.executor(Runnable::run)
. 这将通过额外驱逐条目来惩罚调用者,但不会阻止其他操作的发生。这是由于缓存内部使用了多个锁和操作缓冲区,因此它可以在锁忙时调度工作而不是阻塞线程。
关于大小,这是因为条目在被检索之前被驱逐,因此它不会增加频率。如果条目不存在, AgetIfPresent
不会增加频率,而get(key, /* loading function */)
会因为在未命中时加载值而受到惩罚。驱逐政策在其决策中同时利用了新近度和频率,因此它可能会像经常“一击奇迹”一样提前驱逐最近到达的人,也就是缓存污染。
如果我们按原样获取您的代码并输出缓存的状态,我们会看到这一点,
for (int i = 1; i <= 10; i++) {
String val = String.valueOf(i);
cache.put(val, val);
System.out.println(val + " -> " + cache.asMap());
if (i % 2 == 0) {
cache.getIfPresent("5");
}
}
cache.cleanUp();
System.out.println("cache size: " + cache.estimatedSize());
1 -> {1=1}
2 -> {1=1, 2=2}
3 -> {1=1, 2=2, 3=3}
4 -> {2=2, 3=3, 4=4}
5 -> {2=2, 3=3, 5=5}
6 -> {2=2, 3=3, 6=6}
7 -> {2=2, 3=3, 7=7}
8 -> {2=2, 3=3, 8=8}
9 -> {2=2, 3=3, 9=9}
10 -> {2=2, 3=3, 10=10}
cache size: 3
如果我们5
在每次迭代中访问密钥,那么它就会被保留,
for (int i = 1; i <= 10; i++) {
String val = String.valueOf(i);
cache.put(val, val);
System.out.println(val + " -> " + cache.asMap());
cache.getIfPresent("5");
}
cache.cleanUp();
System.out.println("cache size: " + cache.estimatedSize());
1 -> {1=1}
2 -> {1=1, 2=2}
3 -> {1=1, 2=2, 3=3}
4 -> {2=2, 3=3, 4=4}
5 -> {2=2, 3=3, 5=5}
6 -> {3=3, 5=5, 6=6}
7 -> {3=3, 5=5, 7=7}
8 -> {3=3, 5=5, 8=8}
9 -> {3=3, 5=5, 9=9}
10 -> {3=3, 5=5, 10=10}
cache size: 3
推荐阅读
- javascript - html 输入框在打印页面中未对齐
- bash - 获取 if 语句中的命令输出到变量中
- spring-boot - 如何调试 Spring Security Oath2 和 Jwt 以了解其工作原理
- ansible - 运行ansible playbook时遇到“无法访问”“权限被拒绝”错误
- java - 在 Eclipse 中使用正则表达式在 xml 文件中查找查询
- python - 为什么每个线程都得到一个新的 packageManager 对象副本?我怎么可以只用一个?
- azure-devops - ADO 项目的存档选项
- android - 在 Android 上的 NavigationButton 中将大小设置为图标
- java - WebClient 请求级别超时抛出称为默认 onErrorDropped 的运算符
- php - 转化上传:如何在每次转化上传时设置转化窗口?