预加载番石榴缓存的值

时间:2011-10-27 11:08:50

标签: java caching guava

我需要从数据库加载静态数据以便在Java应用程序中使用。任何缓存机制都应具有以下功能:

  • 从数据库加载所有静态数据(加载后,此数据不会更改)
  • 从数据库加载新数据(启动时数据库中存在的数据不会改变,但可以添加新数据)

所有数据的延迟加载都不是一个选项,因为应用程序将部署到多个地理位置,并且必须与单个数据库通信。延迟加载数据将使特定元素的第一个请求太慢,而应用程序位于数据库的不同区域。

我一直在Guava中使用MapMaker API成功,但我们现在正在升级到最新版本,我似乎无法在CacheBuilder API中找到相同的功能;我似乎无法找到一种在启动时加载所有数据的简洁方法。

一种方法是从数据库加载所有密钥并分别通过缓存加载。这可以工作,但会导致对数据库的N + 1调用,这不是我正在寻找的有效解决方案。

public void loadData(){
    List<String> keys = getAllKeys();
    for(String s : keys)
        cache.get(s);
}

或者其他解决方案是使用ConcurrentHashMap实现并自己处理所有线程和缺少的条目?我并不热衷于这样做,因为MapMaker和CacheBuilder API免费提供基于密钥的线程锁定,而无需提供额外的测试。我也很确定MapMaker / CacheBuilder实现会有一些我不知道/没有时间进行调查的效率。

public Element get(String key){
    Lock lock = getObjectLock(key);
    lock.lock();
    try{
        Element ret = map.get(key)
        if(ret == null){
            ret = getElement(key); // database call
            map.put(key, e);
        }
        return ret;
    }finally {
        lock.unlock();
    } 
}

有人可以考虑更好地解决我的两个要求吗?


功能请求

我不认为预加载缓存是一种不常见的要求,因此如果CacheBuilder提供了一个预加载缓存的配置选项,那就太好了。我认为提供一个接口(很像CacheLoader),它将在启动时填充缓存,这将是一个理想的解决方案,例如:

CacheBuilder.newBuilder().populate(new CachePopulator<String, Element>(){

    @Override
    public Map<String, Element> populate() throws Exception {
        return getAllElements();
    }

}).build(new CacheLoader<String, Element>(){

    @Override
    public Element load(String key) throws Exception {       
        return getElement(key);
    }

});

此实现将允许使用所有相关的Element对象预先填充Cache,同时保持底层CustomConcurrentHashMap对外部世界不可见。

2 个答案:

答案 0 :(得分:7)

在短期内我会使用Cache.asMap().putAll(Map<K, V>)

一旦发布了Guava 11.0,您可以使用Cache.getAll(Iterable<K>),这将为所有缺席元素发出一个批量请求。

答案 1 :(得分:4)

我将从数据库加载所有静态数据,并使用cache.asMap().put(key, value)将其存储在缓存中([Guava 10.0.1允许在Cache.asMap()视图上执行写操作] [1]。 / p>

当然,如果您的缓存配置为逐出条目,则此静态数据可能会被逐出......

CachePopulator的想法很有意思。