如何仅基于番石榴中外部存储的结果来缓存一些活动数据?

时间:2018-08-01 04:10:26

标签: java caching guava google-guava-cache

这里是背景:我的外部存储中有10亿用户,并且大多数用户每天至少要访问一次,但是只有一些活动数据可以访问更多。

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

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

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

所以我希望控制番石榴,告诉它该数据不是要缓存的,就像这样:

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

在番石榴中能否实现这一目标?

2 个答案:

答案 0 :(得分:1)

根据Guava Cache Explanation,如果您通过Cache.get获取对象,则无法防止将其缓存。

因此,有两种方法可以解决此问题:

1)使用Cache.getIfPresent检索缓存外部的值,并使用Cache.putInserted directly)直接插入它们:

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

2)从Cache.invalidateCache.get)获得Explicit removals后,立即使用Ben's suggestion从缓存中删除无效值:

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

编辑:实际上,还有第三种解决方法,但这是比{{3}}更大的破解方法:

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

答案 1 :(得分: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)

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