2015-11-18 7 views
1

При работе с объектами пользователя, поступающими из базы данных, обычно есть идентификатор и имя пользователя, и обычным является поиск пользователя по идентификатору или по имени пользователя.Ключи Guava с использованием независимых ключей

Если я сейчас хочу найти пользователей и люблю использовать кешивы Guava, я должен создать два кэша. Один - кеширование по id, один - кеширование по имени пользователя.
Но оба указывают на один и тот же объект.

Можно ли использовать только один загрузочный кеш?

Я думал об использовании самого объекта User как ключа LoadingCache<User, User> и реализовал равные и хэш-коды в объекте User.
В методе равных нетрудно сказать, что два объекта User равны, если либо id , либо имя пользователя равно.
Но как я могу создать хороший метод hashCode, который работает для этого сценария?

Любые идеи по этому поводу?

+0

Мне непонятно _why_ вы хотите использовать только один кеш. Использование двух кешей кажется правильным в этом сценарии. (И заставить ваш метод равных работать так, что это определенно сломает вещи, «равно» ожидается переходным, а ваша логика - нет.) –

+0

В предшественнике Guava Cache у меня был пример [индексируемого кеша] (https: // code.google.com/p/concurrentlinkedhashmap/wiki/IndexableCache), которые предоставили эту возможность. Его слишком редко нужно было, чтобы мы не вводили эту концепцию в Гуаву. Основная идея может быть адаптирована в вашем собственном коде, хотя (например, с помощью Guave's Striped). –

ответ

1

Благодарим вас за ответы, особенно от самого разработчика Guava. Предложить решение было для меня много работать, я ленив;).

Так что, если я, тем не менее, буду иметь кэш, я решил решить его таким образом.

final LoadingCache<Serializable, Optional<ITemplate>> templatesById = CacheBuilder.newBuilder() 
     .maximumSize(MAX_CACHE_SIZE).expireAfterAccess(MAX_CACHE_LIFE_TIME, TimeUnit.MINUTES) 
     .build(new CacheLoader<Serializable, Optional<ITemplate>>() { 

      @Override 
      public Optional<ITemplate> load(final Serializable id) { 
       final ITemplate template = readInternal(id); 
       final Optional<ITemplate> optional = Optional.ofNullable(template); 
       if (template != null) { 
        templatesByKey.put(template.getKey(), optional); 
       } 
       return optional; 
      } 
     }); 

final LoadingCache<String, Optional<ITemplate>> templatesByKey = CacheBuilder.newBuilder() 
     .maximumSize(MAX_CACHE_SIZE).expireAfterAccess(MAX_CACHE_LIFE_TIME, TimeUnit.MINUTES) 
     .build(new CacheLoader<String, Optional<ITemplate>>() { 

      @Override 
      public Optional<ITemplate> load(final String key) { 
       final ITemplate template = byKeyInternal(key); 
       final Optional<ITemplate> optional = Optional.ofNullable(template); 
       if (template != null) { 
        templatesById.put(template.getId(), optional); 
       } 
       return optional; 
      } 
     }); 

Это означает, что у меня нет памяти для двух экземпляров шаблона в двух кешах. Поэтому я просто добавляю шаблон в оба кэша, если он получен из базы данных.

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

@Override 
@Transactional 
public void update(final ITemplate template) { 
    super.update(new DBTemplate(template)); 
    templatesById.invalidate(template.getId()); 
    templatesByKey.invalidate(template.getKey()); 
} 

Всё.
Любые комментарии к этому вопросу?

+1

Это именно концепция решения «заселение сестринских кешей из загрузчика», я описал и обсудил в своем ответе вместе с «Противниками». Основная проблема заключается в согласованности, поскольку нет защиты от того, что две ссылки в кэша указывают на разные объекты для определенного момента времени. Если вы правильно оформляете границы транзакций, а byKeyInternal и readInternal имеют контекст транзакции два, вы можете обойти это. – cruftex

+0

Почему они должны указывать на разные объекты? Они заполнены только кодом, который вы видите. Таким образом, этот код добавляет один объект в оба кэша. Какая разница? – Nabor

+0

Я сказал на конкретный момент времени. Это состояние гонки. Поставленный кэш-кеш заканчивается до завершения загрузки. – cruftex

1

При работе с объектами пользователя, поступающими из базы данных, обычно есть идентификатор и имя пользователя, и обычным является поиск пользователя по идентификатору или по имени пользователя.

Примечание: «поиск» означает что-то другое для меня, а затем доступ. Возможно, идентификатор и имя пользователя имеют разные шаблоны использования? Возможно, имя пользователя требуется только во время входа в систему?

Избегайте использования двух разных концепций для ссылки на пользователя в приложении. Решите для одного использования его последовательно. Является ли имя пользователя уникальным? Может ли это измениться?

Два кэша: вы можете использовать два кеша и заполнить «сестра-кеш» с загрузчика name2user.put(user.getName(), user) или id2user.put(user.getId(), user). Таким образом, идентичный пользовательский объект находится в обоих кэшах. Тем не менее, мне это не нравится, из-за чистоты и непротиворечивости.

Третья проблема - дублирование данных, если вы решите перейти на другое решение. Кэш может хранить значение не по ссылке, а копировать его в компактные байт-массивы и хранить его вне кучи (EHCache3, Hazelcast и т. Д.). (Clean) не должен полагаться на то, что кэш хранит свои данные по ссылке в куче, если в этом нет никакой реальной необходимости.

Как предполагалось выше, два пути доступа не будут использоваться при использовании. Моя рекомендация:

  • Один кэш для кэширования данных пользователя с: id -> User
  • Второго кэшем для разрешения только идентификатора: name -> id

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

Смежные вопросы