мой идеальный кеш с использованием гуавы - PullRequest
20 голосов
/ 21 июля 2011

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

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

8 голосов
/ 25 июля 2011

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

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

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

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

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

-dg

0 голосов
/ 25 июля 2011

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

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

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

...