Предварительная загрузка значений для кэша Guava - PullRequest
23 голосов
/ 27 октября 2011

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

  • Загрузить все статические данные из базы данных (после загрузки эти данные не изменятся)
  • Загрузка новых данных из базы данных (данные, присутствующие в базе данных при запуске, не изменятся, но есть возможность добавить новые данные)

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

Я успешно использовал API MapMaker в Гуаве, но сейчас мы обновляем его до последней версии, и я не могу найти ту же функциональность в API CacheBuilder; Я не могу найти чистый способ загрузки всех данных при запуске.

Один из способов - загрузить все ключи из базы данных и загрузить их через Cache по отдельности. Это бы сработало, но привело бы к N + 1 вызовам в базу данных, что не совсем эффективное решение, которое я ищу.

public void loadData(){
    List<String> keys = getAllKeys();
    for(String s : keys)
        cache.get(s);
}

Или другое решение - использовать реализацию ConcurrentHashMap и обрабатывать все потоки и недостающие записи самостоятельно? Я не заинтересован в этом, поскольку API-интерфейсы MapMaker и CacheBuilder обеспечивают блокировку потоков на основе ключей бесплатно без необходимости дополнительного тестирования. Я также уверен, что реализации MapMaker / CacheBuilder будут иметь некоторые преимущества, о которых я не знаю / у которых нет времени исследовать.

public Element get(String key){
    Lock lock = getObjectLock(key);
    lock.lock();
    try{
        Element ret = map.get(key)
        if(ret == null){
            ret = getElement(key); // database call
            map.put(key, e);
        }
        return ret;
    }finally {
        lock.unlock();
    } 
}

Кто-нибудь может придумать лучшее решение для моих двух требований?


Запрос функций

Я не думаю, что предварительная загрузка кэша является редким требованием, поэтому было бы неплохо, если бы CacheBuilder предоставил опцию конфигурации для предварительной загрузки кэша. Я думаю, что предоставление интерфейса (очень похожего на CacheLoader), который будет заполнять кеш при запуске, был бы идеальным решением, например:

CacheBuilder.newBuilder().populate(new CachePopulator<String, Element>(){

    @Override
    public Map<String, Element> populate() throws Exception {
        return getAllElements();
    }

}).build(new CacheLoader<String, Element>(){

    @Override
    public Element load(String key) throws Exception {       
        return getElement(key);
    }

});

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

Ответы [ 2 ]

7 голосов
/ 28 октября 2011

В краткосрочной перспективе я бы просто использовал Cache.asMap().putAll(Map<K, V>).

После выхода Guava 11.0 вы можете использовать Cache.getAll(Iterable<K>), который выдаст один массовый запрос для всех отсутствующих.элементы.

4 голосов
/ 27 октября 2011

Я бы загружал все статические данные из БД и сохранял их в Cache, используя cache.asMap().put(key, value) ([Guava 10.0.1 разрешает операции записи в представлении Cache.asMap ()] [1]).

Конечно, эти статические данные могут быть удалены, если ваш кэш настроен на удаление записей ...

Идея CachePopulator интересна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...