мой идеальный кэш с помощью guava

на протяжении последних нескольких недель я пытался найти мой идеал реализации кэша, используя гуава это картографа. См. мои предыдущие два вопроса здесь и здесь следить за моим мыслительным процессом.

принимая то, что я узнал, моя следующая попытка будет отбросить мягкие значения в пользу maximumSize и expireAfterAccess:

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

здесь

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

тем не менее, одна оставшаяся проблема, которую я все еще борюсь с тем, что эта реализация будет выселять объекты, даже если они сильно достижимы, как только их время истекло. Это может привести к нескольким объектам с одним и тем же UID, плавающим в среде, чего я не хочу (я считаю, что то, что я пытаюсь достичь, известно как канонизация).

насколько я могу сказать, единственный ответ-иметь дополнительную карту, которая функционирует как интернер, который я могу проверить, если объект данных все еще находится в память:

ConcurrentMap<String, MyObject> interner = new MapMaker()
        .weakValues()
        .makeMap();

и функция нагрузки будет пересмотрена:

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;
   }
};

однако использование двух карт вместо одной для кэша кажется неэффективным. Есть ли более утонченный способ подойти к этому? В общем, правильно ли я это делаю, или я должен переосмыслить свою стратегию кэширования?

2 ответов


эффективность двух карт полностью зависит от того, насколько дорог getFromDatabase() и насколько велики ваши объекты. Это не кажется из всех разумных границ, чтобы сделать что-то подобное.

Что касается реализации, похоже, что вы, вероятно, можете наложить свои карты немного другим способом, чтобы получить нужное поведение, и все еще иметь хорошие свойства параллелизма.

  1. создайте свою первую карту со слабыми значениями и поместите вычислительную функцию getFromDatabase () на этой карте.
  2. вторая карта является истекающей, также вычислительной, но эта функция просто получает от первой карты.

У всех свой доступ через вторую карту.

другими словами, истекающая карта действует, чтобы закрепить наиболее недавно использованное подмножество ваших объектов в памяти, в то время как карта слабой ссылки является реальным кэшем.

- dg


Я не понимаю полную картину здесь, но две вещи.

  1. учитывая это утверждение: "эта реализация будет выселять объекты, даже если они сильно достижимы, как только их время истекло. Это может привести к нескольким объектам с одним и тем же UID, плавающим в среде, чего я не хочу."...похоже, вам просто нужно использовать weakKeys() и не использовать ни Временное, ни размерное выселение.

  2. или если вы хотите принести "интернер" в этом, я бы использовал настоящий Interners.newWeakInterner.