寻找java.util.Map的替代品

时间:2011-01-18 16:23:03

标签: java caching hadoop ehcache berkeley-db

问题

跟进this question,似乎基于文件或磁盘的Map实现可能是我在那里提到的问题的正确解决方案。简短版本:

  • 目前,我已Map实施为ConcurrentHashMap
  • 以相当固定的速率不断添加条目。稍后详细说明。
  • 最终,无论如何,这意味着JVM会耗尽堆空间。

在工作中,(强烈)建议我使用SQLite解决此问题,但在询问上一个问题之后,我认为数据库不是这项工作的正确工具。所以 - 让我知道这听起来是否疯狂 - 我认为更好的解决方案是存储在磁盘上的Map

糟糕的主意:自己实施。更好的主意:使用别人的图书馆! 哪一个?

要求

必备:

  • 持久。数据需要在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?我以前从未使用过它。这是我之前提问的suggested solution
  • Berkeley DB?再说一遍,我从来没有使用它,我真的对此一无所知。
  • Hadoop(以及哪个子项目)?没用过它。基于these docs,它的跨平台准备对我来说是模棱两可的。在可预见的将来,我不需要分布式操作。
  • 毕竟是SQLite JDBC driver
  • ???

Ehcache和Berkeley DB现在看起来都很合理。在任何一个方向都有任何特别的建议吗?

6 个答案:

答案 0 :(得分:8)

更新(首次发布后约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

答案 1 :(得分:5)

您是否从未听说过流行框架?

编辑对该术语的一些澄清。

就像现在所说的James Gosling一样,没有SQL DB像内存存储一样高效。 Prevalence框架(最知名的是prevaylerspace4j)建立在内存上,可能存储在磁盘上的存储上。他们是如何工作的?事实上,它看似简单:存储对象包含所有持久性实体。此存储只能通过可序列化操作进行更改。因此,将对象放入存储是在隔离的上下文中执行的Put操作。由于此操作是可序列化的,因此它可以(取决于配置)也保存在磁盘上以实现长期持久性。但是,主要的数据存储库是内存,这无疑会提高访问时间,但代价是内存使用率很高。

另一个优点是,由于它们非常简单,这些框架几乎不包含十分之一以上的类

考虑到你的问题,我立即想到Space4J的使用(因为它为很少使用的对象提供了支持&#34;钝化&#34;可以说它们的索引键是在内存中,只要它们不被使用,对象就会保存在磁盘上。

请注意,您还可以在c2wiki找到一些信息。

答案 2 :(得分:1)

Berkeley DB Java Edition有一个Collections API。在该API中,特别是StoredMap是ConcurrentHashMap的替代品。在创建StoredMap之前,您需要创建环境和数据库,但Collections tutorial应该可以很容易。

根据您的要求,Berkeley DB易于使用,我认为您会发现它具有出色的可扩展性和性能。 Berkeley DB在开源许可下可用,它是持久的,独立于平台的,允许您搜索数据。根据需要,当然可以清除/删除数据。 Berkeley DB有很多其他功能,您可能会发现这些功能对您的应用程序非常有用,特别是随着您的需求随着应用程序的成功而变化和增长。

如果您决定使用Berkeley DB Java版,请务必在BDB JE Forum上提问。有一个活跃的开发者社区,很乐意帮助回答问题并解决问题。

答案 3 :(得分:0)

我们使用Xapian实施了类似的解决方案。它速度快,可扩展,几乎可以证明您所要求的所有搜索功能,它是免费的,多平台的,当然还可以清除。

答案 4 :(得分:0)

几个星期前,我来到了jdbm2。用法很简单。您应该能够在半小时内完成工作。一个缺点是放入映射的对象必须是可序列化的,即实现Serializable。其他缺点在他们的网站上给出。

但是,所有对象持久性数据库都不是存储您自己的java类对象的永久解决方案。如果您决定对类的字段进行更改,则将无法再从地图集合中重新创建对象。它是存储标准可序列化行StringInteger

的理想选择

答案 5 :(得分:0)

google-collections库是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;
        }
    });

这将为您提供一个Map缓存,它将自行清理并可以计算其值。如果你能够很好地计算那样的值,那么它就会完美地映射到你要写的http://redis.io/上(公平地说,redis本身可能足够快!)。