寻找java.util.Map的替代品

问题

关注这个问题 ,似乎基于文件或磁盘的Map实现可能是我在那里提到的问题的正确解决方案。 简洁版本:

  • 现在,我有一个实现为ConcurrentHashMapMap
  • 条目以相当固定的速率不断添加到其中。 稍后详细说明。
  • 最终,无论如何,这意味着JVM耗尽了堆空间。

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

不好的主意:自己实现这个。 更好的主意:使用别人的图书馆! 哪一个?

要求

必备:

  • 自由。
  • 持久性。 数据需要在JVM重启之间保持不变。
  • 某种可搜索性。 是的,我需要能够检索这些数据以及将其丢弃。 基本结果集过滤是一个加号。
  • 与平台无关。 需要在Windows或Linux计算机上进行生产部署。
  • 可以清除 。 磁盘空间是有限的,就像堆空间一样。 我需要摆脱n天的条目。 如果我必须手动执行此操作,这不是什么大问题。

尼斯对富人:

  • 使用方便。 如果我能在本周末完成这项工作,那将是很棒的。
    更好的是:一天结束。 如果我可以在我的类路径中添加一个JAR,更改new ConcurrentHashMap();真的非常非常棒new ConcurrentHashMap();new SomeDiskStoredMap();
    并完成。
  • 体面的可扩展性和性能。 最坏的情况:新的条目(平均)每秒增加3次,每秒,每天,每天。 但是,插入并不总是顺利发生。 它可能是(no inserts for an hour)然后(insert 10,000 objects at once)

可能的解决方案

  • Ehcache ? 我以前从未使用过它。 这是我之前的问题的建议解决方案 。
  • 伯克利数据库 ? 我再也没用过它,我真的对此一无所知。
  • Hadoop (以及哪个子项目)? 没用过它。 基于这些文档 ,它的跨平台准备对我来说是模棱两可的。 在可预见的将来,我不需要分布式操作。
  • 毕竟是SQLite JDBC驱动程序 ?
  • ???

Ehcache和Berkeley DB现在看起来都很合理。 任何方向的任何特定建议?

更新(首次发布后约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 implements Map { @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> 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 keySet() { List 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 values() { throw new UnsupportedOperationException(); } // end method @Override public void putAll(Map m) { for( K key : m.keySet() ) { this.put(key, m.get(key)); } // end for } // end method } // end class 

你有没有听说过流行框架?

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

就像James Gosling现在所说的那样,没有SQL DB像内存存储一样高效。 流行框架(最常见的是prevayler和space4j )建立在内存上,可能存储在磁盘上的存储上。 他们是如何工作的? 事实上,它看似简单:存储对象包含所有持久性实体。 此存储只能通过可序列化操作进行更改。 因此,将对象放入存储器是在隔离的上下文中执行的Put操作。 由于此操作是可序列化的,因此可以(根据配置)将其保存在磁盘上以实现长期持久性。 然而,主数据存储库是存储器,其以高内存使用为代价提供了无疑的快速访问时间。

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

考虑到你的问题,我立即想到了Space4J的使用(因为它提供了对很少使用的对象的“钝化”的支持,也就是说它们的索引键在内存中,但是只要它们是对象就保存在磁盘上不曾用过)。

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

Berkeley DB Java版具有Collections API。 在该API中,特别是StoredMap是ConcurrentHashMap的替代品。 在创建StoredMap之前,您需要创建环境和数据库,但是Collections教程应该非常简单。

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

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

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

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

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

google-collections库是http://code.google.com/p/guava-libraries/的一部分,它有一些非常有用的地图工具。 特别是MapMaker允许您使用定时驱逐来生成并发HashMaps,如果您的堆耗尽,以及计算function,垃圾收集器将扫描软值。

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

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