如何仅根据Guava外部存储的结果缓存一些活动数据



这是背景:我的外部存储中有10亿用户,其中大多数用户每天至少会被访问一次,但只有一些活动数据会被访问得更多。

所以对于Guava,我可以写:

cache.get(key, new Callable() {
Call() {
return getExternal(key);        
}
});

但是,每次我从外部存储器加载时,Guava都会将对象缓存到内存中。但由于我有一个非常大的数据集,非常不活跃的数据也会加载到内存中,然后超过最大大小,因此真正活跃的数据可能会被消除。

所以我希望控制Guava,告诉它这些数据不打算被缓存,比如:

cache.get(key, new Callable() {
Call() {
MyObject o = getExternal(key);      
if (!o.isActive())   {
...//do NOT cache
}
}
});

在瓜瓦有可能实现这个目标吗?

这是一个很好的与缓存相关的一般问题,所以请原谅我,如果我稍微扩大了范围,而不仅仅是就Guava Cache提供建议。

if (!o.isActive())   {
...//do NOT cache
}

首先,你真的确定你需要进行这种优化,这会有一些好处吗?缓存驱逐算法已经在做你想要实现的事情:它将更频繁请求的数据保留在缓存中,并驱逐不再请求的数据。如果您不想在缓存中有那么多非活动数据,那么降低缓存大小可能是最简单的解决方案。使用LRU驱逐算法的缓存,如Guava,在驱逐未使用的数据时速度相当慢,因为条目需要"向下移动"整个LRU列表。使用更现代算法(如咖啡因或cache2k(的缓存可以更快地清除未使用的数据。

另一种方法是在访问后设置过期。因此,如果一个条目在给定的持续时间内没有被周期性地请求,它就会过期,然后在一段时间后从缓存中删除。

如果您想根据读取的数据控制缓存行为,Guava缺少其他缓存提供的一个功能,即基于缓存值的可变到期时间。对于cache2k,您可以在构建缓存时添加以下规则,该规则将使活动条目保持5分钟,并立即使其他条目过期:

builder.expiryPolicy((key, value, loadTime, oldEntry) -> 
value.isActive() ? TimeUnit.MINUTES.toMillis(5) : Expiry.NOW)

Caffene和EHCache也可以采用类似的方法。

根据Guava Cache解释,如果通过Cache.get获取对象,则无法阻止缓存该对象。

所以有两种方法可以处理:

1( 使用Cache.getIfPresent检索缓存外的值,并使用Cache.put直接插入(直接插入(:

MyObject o = cache.getIfPresent(key);
if (o == null) {
o = getExternal(key);
if (o.isActive()) {
cache.put(key, o);
}
}

2( 从Cache.get获取非活动值后,立即使用Cache.invalidate从缓存中删除该值(显式删除(:

MyObject o = cache.get(key, () -> getExternal(key));
if (!o.isActive()) {
cache.invalidate(key);
}

EDIT:实际上还有第三种方法可以实现,但这是一个比Ben建议的更大的破解:

MyObjectHolder holder = new MyObjectHolder();
cache.asMap().compute(key, holder::computeActive); // discards the result of compute()
MyObject o = holder.result;

其中MyObjectHolder:

private static class MyObjectHolder {
MyObject result = null;
MyObject computeActive(String key, MyObject oldValue) {
if (oldValue != null) {
result = oldValue;
return oldValue;
}
result = getExternal(key);
return result.isActive() ? result : null; // cache only active values
}
}

最新更新