Java项目:使HashMap(包括加载存储)性能更好

Java项目:使HashMap(包括加载存储)性能更好,java,hashmap,key-value,Java,Hashmap,Key Value,我正在尝试为我们的服务器编码,在这个服务器中,我必须通过URL查找用户访问类型 现在,在一开始,我们看到每天有1亿个不同的URL被访问。现在,随着时间的推移,它已经成为每天近6亿个不同的URL 对于1亿人来说,我们所做的是: 1) 使用并行数组构建HashMap,该数组的键是URL的一部分(表示为LONG),值是URL的另一部分(表示为INT)——key可以有多个值 2) 然后搜索HashMap以查找URL被访问的时间 现在,随着哈希表变得越来越大,我们所做的是: 1) 构建两个或三个单独的哈希

我正在尝试为我们的服务器编码,在这个服务器中,我必须通过URL查找用户访问类型

现在,在一开始,我们看到每天有1亿个不同的URL被访问。现在,随着时间的推移,它已经成为每天近6亿个不同的URL

对于1亿人来说,我们所做的是:

1) 使用并行数组构建HashMap,该数组的键是URL的一部分(表示为LONG),值是URL的另一部分(表示为INT)——key可以有多个值

2) 然后搜索HashMap以查找URL被访问的时间

现在,随着哈希表变得越来越大,我们所做的是:

1) 构建两个或三个单独的哈希表,加载并存储它(在通用文件系统上),以查找URL访问的次数

现在的问题是,

1) 虽然哈希表的性能非常好,但代码在加载/存储哈希表时需要花费更多的时间(我们使用的是文件通道,加载/存储哈希表需要16-19秒—2亿个条目—因为加载系数为0.5)

我们想问的是:

1) 对如何解决这个问题有何评论

2) 如何减少加载/存储时间(我以前问过,但似乎文件通道是最好的方法)

3) 存储一个大的哈希表(多于内存)并重复缓存它是一个不错的解决方案吗?如果是这样,如何做到这一点(至少有一些指针)。我们试着用

RandomAccessFile raf = new RandomAccessFile("array.dat", "rw");
IntBuffer map = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1 << 30).order(ByteOrder.nativeOrder()).asIntBuffer();
RandomAccessFile raf=新的RandomAccessFile(“array.dat”、“rw”);

IntBuffer map=raf.getChannel().map(FileChannel.MapMode.READ_WRITE,0,1您可以使用缓存框架,比如.1亿个键值对应该不会有问题


一定要试试,认为它可以轻而易举地击败任何其他东西

您可以使用它,它基本上是一个用C编写的键/值存储,以实现最高性能。它是Oracle产品(尽管是开源的)所以我会严肃对待。

整个方法听起来很可笑。我想你真正想要实现的是每个不同URL的简单访问计数器。就其本质而言,这些数据经常被写入,但很少被读取

为此,我只需要有一个数据库表,并为每次访问添加一个新条目(它也可以用作日志)。当您需要计算访问任何URL的频率时,可以使用表中的SELECT计数轻松完成此操作(根据URL条目存储的额外数据量,您甚至可以进行限制计数,如昨天、上周等的访问频率)

这就把所有的工作推到了真正需要结果的地方


顺便说一句,您也可以从web服务器日志文件中检索访问计数,因此您可能不需要自己编写任何数据。请首先查看此信息。

如果您的应用程序必须在不使用任何外部计算能力的情况下在本地运行, 没有比直接内存访问更高效的解决方案:唯一能提供比哈希映射更好性能的数据结构是数组,其中每个元素的访问都是O(1)但是,这需要事先知道您有多少项,每个元素有一个唯一的寻址索引,并且能够保留重要的相邻内存

数组(如上所述适用于有限的情况)之后,您就有了哈希表,但是随着数据大小的增长,冲突和动态调整大小的成本会增加,并使性能变差

您可以参考java.util.HashMap javadoc,也可以参考Wikipedia了解以下内容:

  • 计算它有多贵
  • 价值是如何分布的
  • 您使用的负载系数是多少,即冲突解决的成本是多少
  • 在HashMap完全包含所有数据之前,您需要多久调整一次它的大小
如果在构建HashMap时性能下降,我实际上认为这是一个ConcurrentHashMap(如果并行构建它,它必须是线程安全的),那么您可能需要调查为什么会发生这种情况

一个简单但容易的开始是用树映射替换HashMap,树映射的性能是其大小的确定函数,并比较这两种性能


另一方面,如果我曲解了你的问题,你有机会在多台机器上扩展计算,那么正如有人指出的那样,市场上有很多有趣的解决方案,我还要加上卡桑德拉


这些解决方案通过在多个节点之间分配负载来提高性能,但在每个节点内部都使用著名的算法进行快速高效的寻址。

问题和后续讨论不清楚,但您的查询的性质是什么?您在这两个节点之间的情况非常不同
a) 在每个工作日内浏览所有~7亿个URL,或
b) 在这些约7亿个URL中找到了一小部分

那么:查询的数量与URL的数量之比是多少

根据您的描述,听起来您可能正在加载/卸载代表阵列不同部分的不同文件。。。这表示随机查询,这表示(b)

另外,我想您已经认识到“全部存储在内存中”是不可行的(即,您已将阵列拆分为多个文件),因此最佳的磁盘访问算法似乎是下一个任务,不是吗


对于每个查询,您是否尝试过一次简单的查找(n*arrayElementSize)以在文件中进行偏移,并将几页读入内存(您是否有/知道每个键的最大值?)。您已经将基本索引(计算)放入数组中,因此这应该很容易原型化。

最好将表作为缓冲区访问。这样,您就可以实现对文件的随机访问,而不用担心加载和存储,并将缓存留给操作系统。
[(32 + 64) * 600 million] bits i.e. a 53.644 MB structure in memory