我需要从数据库加载静态数据以便在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对外部世界不可见。
答案 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的想法很有意思。