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

Java 如何仅根据Guava中外部存储的结果缓存一些活动数据?,java,caching,guava,google-guava-cache,Java,Caching,Guava,Google Guava Cache,以下是背景:我的外部存储中有10亿用户,其中大多数用户每天至少会被访问一次,但只有一些活动数据会被访问得更多 所以对于番石榴,我可以这样写: cache.get(key, new Callable() { Call() { return getExternal(key); } }); 但是,每次我从外部存储器加载时,Guava都会将对象缓存到内存中。但由于我有一个非常大的数据集,非常不活跃的数据也会加载到内存中,然后超过最大大小,因此真正活跃的数据

以下是背景:我的外部存储中有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
       }
    }
});
在番石榴中有可能实现这一目标吗?

根据,如果通过获取对象,则无法阻止缓存对象

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

1) 使用检索缓存外部的值,并使用()直接插入值:

2) 从()获得非活动值后,立即使用从缓存中删除该值:


编辑:实际上还有第三种方法,但这比:

其中
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
    }
}

这是一个很好的与缓存相关的一般问题,因此如果我稍微扩大了范围,而不仅仅是就番石榴缓存提供建议,请原谅我

   if (!o.isActive())   {
       ...//do NOT cache
   }
首先,你真的确定你需要进行这样的优化,它会带来一些好处吗?缓存逐出算法已经实现了您希望实现的功能:它将更频繁请求的数据保留在缓存中,并逐出不再请求的数据。如果您不希望缓存中有这么多非活动数据,那么降低缓存大小可能是最简单的解决方案。使用LRU逐出算法的缓存(如Guava)逐出未使用数据的速度非常慢,因为条目需要“向下移动”整个LRU列表。使用更现代的算法(如咖啡因或cache2k)的缓存可以更快地清除未使用的数据

另一种方法是在访问后设置到期时间。因此,如果在给定的持续时间内没有定期请求条目,则该条目将过期,并在一段时间后从缓存中删除

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

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

类似的方法在Caffine和EHCache中也是可行的。

,一种番石榴缓存重写,通过返回
null
指示不缓存来支持这一点。它还提供了一个防扫描逐出策略,这在这些情况下可能会有所帮助。一个可能的黑客,虽然取决于内部细节,但使用的是一个称重器,其返回值评估为超过整体最大值的大小。在最近版本的Guava中,缓存将立即丢弃该项,而不是刷新缓存。我现在也在使用这个hack#2,但实际上存在多线程问题。@Jaskey哪种解决方案会给您带来多线程问题<代码>缓存。获取+
缓存。无效
?就我所知,它不应该是这样。
compute
(理想情况下
computeifassent
)是可以的,但我认为番石榴做得非常低效,因为它是搭在架子上的。@BenManes我必须承认我不理解“搭在架子上”这个表达,但我猜它的意思是临时的?但是,
compute
使用,这看起来根本不是临时的。至于
computeIfAbsent
,可以使用它,但需要:返回
computeIfAbsent
的结果(如果它是非空的),或者:返回
holder.result
(仅适用于非活动值)。
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
    }
}
   if (!o.isActive())   {
       ...//do NOT cache
   }
 builder.expiryPolicy((key, value, loadTime, oldEntry) -> 
    value.isActive() ? TimeUnit.MINUTES.toMillis(5) : Expiry.NOW)