我使用番石榴的理想缓存

在过去的几周里,我一直在尝试使用guava的MapMaker找到理想的缓存实现。 请在此处和此处查看我之前的两个问题,以了解我的思维过程。

根据我所学到的,我的下一次尝试将放弃软值,转而使用maximumSize和expireAfterAccess:

ConcurrentMap cache = new MapMaker() .maximumSize(MAXIMUM_SIZE) .expireAfterAccess(MINUTES_TO_EXPIRY, TimeUnit.MINUTES) .makeComputingMap(loadFunction); 

哪里

 Function loadFunction = new Function() { @Override public MyObject apply(String uidKey) { return getFromDataBase(uidKey); } }; 

然而,我仍然在努力解决的另一个问题是,即使它们的时间很长,这个实现也会驱逐对象,即使它们是强烈可达的。 这可能会导致多个对象在环境中浮动相同的UID,这是我不想要的(我相信我想要实现的目标称为规范化)。

所以,据我所知,唯一的答案是有一个额外的地图作为一个内部人员,我可以检查,看看数据对象是否仍在内存中:

 ConcurrentMap interner = new MapMaker() .weakValues() .makeMap(); 

并且将修改加载函数:

 Function loadFunction = new Function() { @Override public MyObject apply(String uidKey) { MyObject dataObject = interner.get(uidKey); if (dataObject == null) { dataObject = getFromDataBase(uidKey); interner.put(uidKey, dataObject); } return dataObject; } }; 

但是,使用两个映射而不是一个缓存似乎效率低下。 有没有更复杂的方法来解决这个问题? 一般来说,我是以正确的方式进行此事,还是应该重新考虑我的缓存策略?

两个映射是否有效取决于getFromDatabase()的价格,以及对象的大小。 做这样的事情似乎没有任何合理的界限。

至于实现,看起来您可能会以稍微不同的方式对映射进行分层以获得所需的行为,并且仍然具有良好的并发属性。

  1. 使用弱值创建第一个地图,并将计算函数getFromDatabase()放在此地图上。
  2. 第二张地图是即将到期的地图,也是计算的,但是这个function只是来自第一张地图。

通过第二张地图进行所有访问。

换句话说,到期的映射用于将最近使用的对象子集固定在内存中,而弱引用映射是真正的缓存。

-DG

我不明白这里的全貌,但有两件事。

  1. 鉴于此声明:“这个实现将驱逐对象,即使它们的时间很长也可以很容易到达。这可能导致多个对象在环境中浮动相同的UID,这是我不想要的。” – 听起来你只需要使用weakKeys()而不是使用定时或基于大小的驱逐。

  2. 或者如果你想把“interner”加入其中,我会使用一个真正的Interners.newWeakInterner