我有一个要求,我们从数据库加载静态数据以便在Java应用程序中使用.任何缓存机制都应具有以下功能:
由于应用程序将部署到多个地理位置并且必须与单个数据库通信,因此不能延迟加载所有数据.延迟加载数据将使应用程序位于数据库的不同区域中对特定元素的第一个请求太慢.
我一直在Guava中使用MapMaker API成功,但我们现在正在升级到最新版本,我似乎无法在CacheBuilder API中找到相同的功能; 我似乎无法找到一种在启动时加载所有数据的简洁方法.
一种方法是从数据库加载所有密钥并单独通过Cache加载它们.这可以工作,但会导致对数据库的N + 1调用,这不是我正在寻找的有效解决方案.
public void loadData(){
List<String> keys = getAllKeys();
for(String s : keys)
cache.get(s);
}
Run Code Online (Sandbox Code Playgroud)
或者另一个解决方案是使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
谁能想到更好的解决方案来满足我的两个要求?
功能要求
我不认为预加载缓存是一种不常见的要求,因此如果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 …Run Code Online (Sandbox Code Playgroud) 过去几周我一直在尝试使用guava的MapMaker找到理想的缓存实现.请在此处和此处查看我之前的两个问题,以了解我的思考过程.
根据我所学到的,我的下一次尝试将放弃软值,转而使用maximumSize和expireAfterAccess:
ConcurrentMap<String, MyObject> cache = new MapMaker()
.maximumSize(MAXIMUM_SIZE)
.expireAfterAccess(MINUTES_TO_EXPIRY, TimeUnit.MINUTES)
.makeComputingMap(loadFunction);
Run Code Online (Sandbox Code Playgroud)
哪里
Function<String, MyObject> loadFunction = new Function<String, MyObject>() {
@Override
public MyObject apply(String uidKey) {
return getFromDataBase(uidKey);
}
};
Run Code Online (Sandbox Code Playgroud)
但是,我仍在努力解决的另一个问题是,即使它们的时间很长,这个实现也会驱逐对象,即使它们是强烈可达的.这可能会导致多个对象在环境中浮动相同的UID,这是我不想要的(我相信我想要实现的目标称为规范化).
所以,据我所知,唯一的答案是有一个额外的映射作为一个interner我可以检查,看看数据对象是否仍在内存中:
ConcurrentMap<String, MyObject> interner = new MapMaker()
.weakValues()
.makeMap();
Run Code Online (Sandbox Code Playgroud)
并且将修改加载函数:
Function<String, MyObject> loadFunction = new Function<String, MyObject>() {
@Override
public MyObject apply(String uidKey) {
MyObject dataObject = interner.get(uidKey);
if (dataObject == null) {
dataObject = getFromDataBase(uidKey);
interner.put(uidKey, dataObject);
}
return dataObject;
} …Run Code Online (Sandbox Code Playgroud)