Ищете замену для java.util.Map. - PullRequest
13 голосов
/ 18 января 2011

Проблема

В ответ на этот вопрос кажется, что реализация Map на основе файлов или дисков может быть правильным решением проблем, которые я там упомянул.Короткая версия:

  • Сейчас у меня Map реализован как ConcurrentHashMap.
  • Записи добавляются в него постоянно, с довольно фиксированной скоростью.Подробности об этом позже.
  • В конце концов, несмотря ни на что, это означает, что JVM не хватает места в куче.

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

Плохая идея: реализовать это самостоятельно.Лучшая идея: использовать чужую библиотеку! Какой?

Требования

Must-haves:

  • Бесплатно.
  • Постоянный. Данные должны храниться между перезапусками JVM.
  • Какой-то поиск. Да, мне нужна возможность извлекать эти проклятые данные, а также помещать ихдалеко.Базовая фильтрация набора результатов является плюсом.
  • Независимо от платформы. Необходимо для развертывания в производственных условиях на машинах Windows или Linux.
  • Очистить .Дисковое пространство конечно, как и куча.Мне нужно избавиться от записей, которые n дней.Ничего страшного, если мне придется делать это вручную.

Приятно иметь:

  • Простота в использовании. Это было быздорово, если я смогу заставить это работать к концу недели.
    Еще лучше: конец дня.Было бы действительно, действительно замечательно, если бы я мог добавить один JAR в мой путь к классу, изменить new ConcurrentHashMap<Foo, Bar>(); на new SomeDiskStoredMap<Foo, Bar>();
    и все было бы сделано.производительность. В худшем случае: новые записи добавляются (в среднем) 3 раза в секунду, каждую секунду, весь день, каждый день.Однако вставки не всегда будут происходить так гладко.Это может быть (no inserts for an hour), затем (insert 10,000 objects at once).

Возможные решения

  • Ehcache ?Я никогда не использовал это раньше.Это было предложенное решение к моему предыдущему вопросу.
  • Berkeley DB ?Опять же, я никогда не использовал его, и я действительно ничего об этом не знаю.
  • Hadoop (и какой подпроект)?Не использовал это.Основываясь на этих документах , его кросс-платформенная готовность неоднозначна для меня.Мне не нужна распределенная операция в обозримом будущем.
  • A Драйвер SQLite JDBC в конце концов?
  • ???

Ehcache и Berkeley DB и сейчас выглядят разумно.Какие-либо конкретные рекомендации в любом направлении?

Ответы [ 6 ]

8 голосов
/ 18 января 2011

ОБНОВЛЕНИЕ (примерно через 4 года после первого сообщения ...): помните, что в более новых версиях ehcache постоянство элементов кэша доступно только в платном продукте. Спасибо @boday за указание на это.

Ehcache это здорово. Это даст вам гибкость, необходимую вам для реализации карты в памяти, на диске или в памяти с вторичным распространением на диск. Если вы используете эту очень простую обертку для java.util.Map, то использовать ее будет просто:

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.log4j.Logger;

import com.google.common.collect.Sets;

public class EhCacheMapAdapter<K,V> implements Map<K,V> {
    @SuppressWarnings("unused")
    private final static Logger logger = Logger
            .getLogger(EhCacheMapAdapter.class);

    public Cache ehCache;

    public EhCacheMapAdapter(Cache ehCache) {
        super();
        this.ehCache = ehCache;
    } // end constructor

    @Override
    public void clear() {
        ehCache.removeAll();
    } // end method

    @Override
    public boolean containsKey(Object key) {
        return ehCache.isKeyInCache(key);
    } // end method

    @Override
    public boolean containsValue(Object value) {
        return ehCache.isValueInCache(value);
    } // end method

    @Override
    public Set<Entry<K, V>> entrySet() {
        throw new UnsupportedOperationException();
    } // end method

    @SuppressWarnings("unchecked")
    @Override
    public V get(Object key) {
        if( key == null ) return null;
        Element element = ehCache.get(key);
        if( element == null ) return null;
        return (V)element.getObjectValue();
    } // end method

    @Override
    public boolean isEmpty() {
        return ehCache.getSize() == 0;
    } // end method

    @SuppressWarnings("unchecked")
    @Override
    public Set<K> keySet() {
        List<K> l = ehCache.getKeys();
        return Sets.newHashSet(l);
    } // end method

    @SuppressWarnings("unchecked")
    @Override
    public V put(K key, V value) {
        Object o = this.get(key);
        if( o != null ) return (V)o;
        Element e = new Element(key,value);
        ehCache.put(e);
        return null;
    } // end method


    @Override
    public V remove(Object key) {
        V retObj = null;
        if( this.containsKey(key) ) {
            retObj = this.get(key);
        } // end if
        ehCache.remove(key);
        return retObj;
    } // end method

    @Override
    public int size() {
        return ehCache.getSize();
    } // end method

    @Override
    public Collection<V> values() {
        throw new UnsupportedOperationException();
    } // end method

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        for( K key : m.keySet() ) {
            this.put(key, m.get(key));
        } // end for
    } // end method
} // end class
5 голосов
/ 18 января 2011

Вы никогда не слышали о распространенности?

РЕДАКТИРОВАТЬ некоторые пояснения к термину.

Как и Джеймс Гослинг теперь говорит, что никакая база данных SQL не является столь же эффективной, как хранилище в памяти. Фреймворки Prevalence (наиболее известными из которых являются prevayler и space4j ) построены на этой идее в памяти, которая может храниться на диске, в хранилище. Как они работают? На самом деле, это обманчиво просто: объект хранения содержит все постоянные объекты. Это хранилище может быть изменено только сериализуемыми операциями. Как следствие, помещение объекта в хранилище является операцией Put , выполняемой в изолированном контексте. Поскольку эта операция является сериализуемой, она может (в зависимости от конфигурации) также сохраняться на диске для длительного хранения. Однако основным хранилищем данных является память, которая обеспечивает, несомненно, быстрое время доступа, за счет высокого использования памяти.

Еще одним преимуществом является то, что из-за своей очевидной простоты эти структуры едва ли содержат более десятой части классов

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

Обратите внимание, что вы также можете найти информацию на c2wiki .

1 голос
/ 19 января 2011

Berkeley DB Java Edition имеет API коллекций. В этом API, в частности, StoredMap, является заменой для ConcurrentHashMap. Вам необходимо создать среду и базу данных перед созданием StoredMap, но учебник Collections должен сделать это довольно просто.

В соответствии с вашими требованиями, Berkeley DB прост в использовании, и я думаю, вы обнаружите, что он обладает исключительной масштабируемостью и производительностью. Berkeley DB доступна по лицензии с открытым исходным кодом, она постоянна, не зависит от платформы и позволяет искать данные. Данные, безусловно, могут быть удалены / удалены по мере необходимости. Berkeley DB имеет длинный список других функций, которые могут оказаться весьма полезными для вашего приложения, особенно когда ваши требования меняются и растут с успехом приложения.

Если вы решили использовать Berkeley DB Java Edition, обязательно задавайте вопросы на форуме BDB JE . Существует активное сообщество разработчиков, которое с радостью ответит на вопросы и решит проблемы.

0 голосов
/ 19 января 2011

В библиотеке Google-коллекций, входящей в http://code.google.com/p/guava-libraries/,, есть несколько действительно полезных инструментов Map. MapMaker , в частности, позволяет вам создавать параллельные HashMaps с синхронизированными вытеснениями, мягкими значениями, которые будут захвачены сборщиком мусора, если у вас заканчивается куча, и вычислительными функциями.

Map<String, String> cache = new MapMaker()
    .softValues()
    .expiration(30, TimeUnit.MINUTES)
    .makeComputingMap(new Function<String, String>() {
        @Override
        public String apply(String input) {
            // Work out what the value should be
            return null;
        }
    });

Это даст вам кэш карты, который будет очищаться после себя и сможет определять его значения.Если вы можете вычислить значения, подобные этим, то отлично, в противном случае он отлично отобразится на http://redis.io/, в который вы будете писать (если быть честным, redis, вероятно, будет достаточно быстрым сам по себе!).

0 голосов
/ 18 января 2011

Я наткнулся на jdbm2 несколько недель назад. Использование очень просто. Вы должны быть в состоянии заставить его работать через полчаса. Один недостаток состоит в том, что объект, который помещается в карту, должен быть сериализуемым, то есть реализовать Serializable. Другие минусы приведены на их сайте.

Однако, все базы данных сохраняемости объектов не являются постоянным решением для хранения объектов вашего собственного java-класса. Если вы решите внести изменения в поля класса, вы больше не сможете получить объект из коллекции карт. Идеально хранить стандартные сериализуемые классы строк String, Integer и т. Д.

0 голосов
/ 18 января 2011

У нас есть аналогичное решение, реализованное с использованием Xapian . Он быстрый, масштабируемый, он обеспечивает практически все функции поиска, которые вы запрашивали, бесплатный, мультиплатформенный и, конечно, очищаемый.

...