Поиск замены для java.util.Map
Проблема
Следуя этому вопросу, кажется, что реализация Map
на основе файлов или дисков может быть правильным решением проблем, о которых я упоминал. Краткая версия:
- Прямо сейчас у меня есть
Map
, реализованный как ConcurrentHashMap
.
- Записи добавляются к нему постоянно, с довольно фиксированной скоростью. Подробности об этом позже.
- В конце концов, несмотря ни на что, это означает, что JVM заканчивается из кучного пространства.
На работе было (решительно) предложено решить эту проблему с помощью SQLite, но, спросив предыдущий вопрос, я не думаю, что база данных является правильным инструментом для этой работы. Итак - сообщите мне, если это звучит безумно. Я думаю, что лучшим решением будет Map
, хранящийся на диске.
Плохая идея: реализовать это самостоятельно. Лучшая идея: используйте чужую библиотеку! Какой?
Требования
Must-имущих:
- Free.
- Постоянный.. Данные должны храниться между перезапусками JVM.
- Некоторая возможность поиска. Да, мне нужна возможность извлекать эти данные, а также удалять их. Фильтрация базового набора результатов является плюсом.
- Независимо от платформы. Необходимо развертывать продукцию на компьютерах под управлением Windows или Linux.
- Purgeable. Дисковое пространство конечно, подобно кучевому пространству. Мне нужно избавиться от записей, которые
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 выглядят разумно прямо сейчас. Какие-либо конкретные рекомендации в любом направлении?
Ответы
Ответ 1
UPDATE (через 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
Ответ 2
Вы никогда не слышали о ракурсах распространенности?
ИЗМЕНИТЬ некоторые пояснения к термину.
Как Джеймс Гослинг теперь говорит, что SQL DB не так эффективна, как память в памяти. Prevalence рамки (наиболее известные из них prevayler и space4j) построены на этой идее встроенной памяти, возможно, сохраняемой на диске, в хранилище. Как они работают? На самом деле это обманчиво просто: объект хранения содержит все постоянные объекты. Это хранилище может быть изменено только с помощью сериализуемых операций. Как следствие, помещение объекта в хранилище - это операция Put, выполняемая в изолированном контексте. Поскольку эта операция сериализуема, она может (в зависимости от конфигурации) также сохраняться на диске для долговременного сохранения. Однако основным репозиторием данных является память, которая обеспечивает, несомненно, быстрое время доступа за счет использования высокой памяти.
Другим преимуществом является то, что из-за их очевидной простоты эти структуры вряд ли содержат более десятой части классов
Учитывая ваш вопрос, использование space4j сразу пришло мне в голову (поскольку оно обеспечивает поддержку "пассивации" редко используемых объектов, что их индексный ключ находится в памяти, но объекты хранятся на диске, пока они не используются).
Обратите внимание, что вы также можете найти некоторую информацию в c2wiki.
Ответ 3
Berkeley DB Java Edition имеет API коллекций. Внутри этого API, StoredMap, в частности, является заменой для ConcurrentHashMap. Вам необходимо создать среду и базу данных, прежде чем создавать StoredMap, но сборник должен сделать это довольно легко.
В соответствии с вашими требованиями Berkeley DB спроектирован так, чтобы быть прост в использовании, и я думаю, что вы обнаружите, что он обладает исключительной масштабируемостью и производительностью. Berkeley DB доступен под лицензией с открытым исходным кодом, он постоянный, независимый от платформы и позволяет вам искать данные. При необходимости данные могут быть очищены/удалены. Berkeley DB имеет длинный список других функций, которые могут оказаться весьма полезными для вашего приложения, особенно по мере того, как ваши требования меняются и растут с успехом приложения.
Если вы решили использовать Berkeley DB Java Edition, обязательно задавайте вопросы на BDB JE Forum. Там активное сообщество разработчиков, которое помогает ответить на вопросы и решить проблемы.
Ответ 4
У нас есть аналогичное решение, реализованное с помощью Xapian. Он быстрый, он масштабируемый, он почти все функции поиска, которые вы запросили, он бесплатный, мультиплатформенный и, конечно, продувочный.
Ответ 5
Несколько недель назад я встретил jdbm2. Использование очень простое. Вы должны иметь возможность заставить его работать через полчаса. Один из недостатков заключается в том, что объект, который помещается в карту, должен быть сериализуемым, т.е. Реализовать Serializable
. Другие минусы указаны на их веб-сайте.
Однако вся база данных сохранения объектов не является постоянным решением для хранения объектов вашего собственного класса Java. Если вы решите внести изменения в поля класса, вы больше не сможете повторно переназначить объект из коллекции карт. Идеально хранить стандартные классы сериализуемых строк String
, Integer
и т.д.
Ответ 6
Библиотека google-collection, часть http://code.google.com/p/guava-libraries/, имеет некоторые действительно полезные инструменты Карты. 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, вероятно, будет достаточно быстрым сам по себе!).