我使用番石榴的理想缓存

Pau*_*ora 20 java caching canonicalization guava

过去几周我一直在尝试使用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)

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

Dar*_*roy 8

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

至于实现,看起来你可能会以稍微不同的方式对你的地图进行分层以获得你想要的行为,并且仍然具有良好的并发属性.

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

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

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

-DG