Подтвердить что ты не робот

Мой идеальный кеш с использованием guava

Выключение и включение в течение последних нескольких недель я пытался найти свою идеальную реализацию кеша, используя guava MapMaker. Смотрите мои предыдущие два вопроса здесь и здесь, чтобы следовать моему мыслительному процессу.

Взяв то, что я узнал, моя следующая попытка будет опускать мягкие значения в пользу 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;
   }
};

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

4b9b3361

Ответ 1

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

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

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

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

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

-dg

Ответ 2

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

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

  • Или, если вы хотите включить в него "интернера", я бы использовал реальный Interners.newWeakInterner.