1

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

所以对于番石榴,我可以写:

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 中实现这个目标?

4

2 回答 2

1

根据Guava Cache Explanation,如果您通过Cache.get.

所以有两种方法可以处理这个问题:

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

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

2)从缓存中删除非活动值,从(显式删除Cache.invalidate)获得它:Cache.get

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

编辑:实际上有第三种方法,但它比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
    }
}
于 2018-08-01T06:22:50.660 回答
1

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

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

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

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

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

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

类似的方法对于 Caffin 和 EHCache 也是可行的。

于 2018-08-01T17:08:04.693 回答