预加载番石榴缓存的值

我有一个要求,我们从数据库加载静态数据以便在Java应用程序中使用。 任何缓存机制都应具有以下function:

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

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

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

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

public void loadData(){ List 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(); } } 

谁能想到更好的解决方案来满足我的两个要求?


function要求

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

 CacheBuilder.newBuilder().populate(new CachePopulator(){ @Override public Map populate() throws Exception { return getAllElements(); } }).build(new CacheLoader(){ @Override public Element load(String key) throws Exception { return getElement(key); } }); 

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

在短期内我会使用Cache.asMap().putAll(Map)

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

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

当然,如果您的缓存配置为逐出条目,这些静态数据可能会被驱逐……

CachePopulator的想法很有趣。