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

Значения предварительной загрузки для кэша Guava

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

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

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

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

Один из способов - загрузить все ключи из базы данных и загрузить их через кэш по отдельности. Это будет работать, но приведет к 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);
    }

});

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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