Java - 自定义哈希映射/表的一些要点

Posted

技术标签:

【中文标题】Java - 自定义哈希映射/表的一些要点【英文标题】:Java - Custom Hash Map/Table Some Points 【发布时间】:2012-07-30 17:15:45 【问题描述】:

在之前的一些帖子中,我问过一些关于 java 中自定义哈希映射/表的编码的问题。现在由于我无法解决它,可能是我忘记正确提及我真正想要的东西,我正在总结所有这些以使其清晰准确。

我要做什么:

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

现在,我有 11.1 亿个 URL(大约)。

那么,我们做了什么,

1) 将数据库划分为 1.1 亿个 Url 的 10 个部分。 2) 使用并行数组构建 HashMap,其键是 URL 的一部分(表示为 LONG),值是 URL 的另一部分(表示为 INT) - 键可以有多个值

3)然后在系统启动时每天开始在HashMap中搜索其他一些URL(一天保存数百万个URL)。

你尝试过的:

1) 我尝试了许多 NoSQL 数据库,但我们发现不太适合我们的目的。

2) 为此,我构建了我们的custom hashmap(使用两个并行数组)。

那么,问题是什么:

当系统启动时,我们必须加载每个数据库的哈希表并执行搜索百万个 url:

现在,问题是,

1) 虽然 HashTable 的性能相当不错,但代码在加载 HashTable 时需要更多时间(我们使用文件通道和内存映射缓冲区来加载它,加载 HashTable 需要 20 秒 - 2.2 亿个条目 - 因为负载因子是0.5,we found it most faster)

所以,我们花费时间:(HashTable Load + HashTable Search)* DB 数量 = (5 + 20) * 10 = 250 秒。这对我们来说相当昂贵,而且大部分时间(250 秒中有 200 秒)用于加载哈希表。

你有没有其他想法:

一种方法可以是:

无需担心加载和存储,通过使用内存映射缓冲区将缓存留给操作系统。但是,由于我必须搜索数百万个键,它的性能比上面的要差。

由于我们发现 HashTable 性能不错,但加载时间很长,我们想以另一种方式将其切断,例如:

1) 创建一个大小为 Integer_MAX (my own custom linked list) 的链表数组。

2) 将值 (int's) 插入到编号为键编号的链表中(我们将键大小减小为 INT)。

3) 所以,我们只需要将链表存储到磁盘上。

现在的问题是,创建如此数量的链接列表需要花费大量时间,如果数据分布不均,创建如此大量的链接列表毫无意义。

那么,您的要求是什么:

简单说一下我的要求:

1) 具有多值插入和搜索的键。寻找良好的搜索性能。 2) 快速加载(特别)到内存中。

(键是 64 位 INT,值是 32 位 INT,一个键最多可以有 2-3 个值。我们也可以将我们的键设置为 32 位,但会产生更多的冲突,但我们可以接受,如果我们可以更好)。

谁能帮助我,如何解决这个问题或任何评论如何解决这个问题?

谢谢。

注意:

1) 根据 Stack Overflow 之前的建议,磁盘缓存的预读取数据是不可能的,因为当系统启动时,我们的应用程序将开始工作,并且在系统启动的第二天。

2) 我们还没有发现 NoSQL 数据库的扩展性很好,因为我们的要求很简单(意味着只需插入哈希表键值并加载和搜索(检索值))。

3) 由于我们的应用程序是小项目的一部分,并且要应用在一个小校园里,我认为没有人会为此给我买一个 SSD 磁盘。那是我的局限。

4) 我们也使用 Guava/Trove,但它们也无法在 16 GB 中存储如此大量的数据(我们使用的是 32 GB 的 ubuntu 服务器。)

【问题讨论】:

你看过 HazelCast 吗?也许它会在这种情况下工作。 hazelcast.com @Marvo,对不起,我应该提到我必须在本地而不是分布式环境方式运行它。这是因为,我真的不是系统设计师,我必须按照我的吩咐去做。 链表方法实际花费了多少时间?仅根据编程竞赛的经验法则,如果任何 Java 实现可能比几分钟快得多,我会……有点震惊。 就个人而言,我很确定不存在明显更好的 Java 实现。我能想到的唯一其他技术增加了令人无法接受的内存成本。 ...好的。我可以考虑进行进一步的更改,但实际上只是减少了内存消耗并稍微降低了速度,所以我认为您不想要它。 @LouisWasserman,我已经编辑了我的新哈希图代码(带有链表)。请您看一下并告诉我是否可以不创建如此大量的链接列表(即即时)。是否可以以这种方式存储它,因此加载速度应该更快。请注意,没有加载键的要求,我们可以在 20 GB 上存储大约 5 亿个键值对。我就是这么想的。 【参考方案1】:

如果您需要快速访问 11.1 亿个数据项,那么散列是您的最佳选择。但不要重新发明***,使用类似的东西:

memcacheDB:http://memcachedb.org MongoDB:http://www.mongodb.org 卡桑德拉:http://cassandra.apache.org

【讨论】:

其实……不同意。如果 OP 希望在 RAM 中执行此操作,他将需要一个定制的数据结构。 听起来 OP 已经为键控计算了一个哈希值,因此所需要做的就是序列化多值的自定义数据结构并根据键存储。 嗯?他也买不起。他说的是他完全定制的数据结构——它甚至不再是一个散列;这只是一个直接的查找表——不够快。【参考方案2】:

在我看来(如果我正确理解您的问题)您正试图以一种复杂的方式解决问题。 我的意思是您尝试预加载的数据一开始就很大(比如说 2.2 亿 * 64 ~ 14GB)。您正在尝试为此进行内存映射等。 我认为这是一个典型的问题,可以通过在不同的机器上分配负载来解决。 IE。而不是试图定位链表索引,您应该尝试找出已加载地图特定部分的相应机器的索引,并从那里获取该机器的值(每台机器都加载了该数据库的一部分地图,您每次都从地图的适当部分(即机器)获取数据)。 也许我离这里很远,但我也怀疑你使用的是 32 位机器。 因此,如果您必须继续使用单机架构并且在经济上不可能改进您的硬件(如您所指出的,64 位机器和更多 RAM 或 SSD),我认为您无法做出任何显着的改进。

【讨论】:

【参考方案3】:

我真的不明白您以什么形式将数据存储在磁盘上。如果您存储的内容由 url 和一些数字组成,您可以通过压缩数据来加快从磁盘加载的速度(除非您已经这样做了)。

创建一个在加载时解压缩的多线程加载程序可能会给您带来很大的提升。

【讨论】:

以上是关于Java - 自定义哈希映射/表的一些要点的主要内容,如果未能解决你的问题,请参考以下文章

ORM对象关系映射之GreenDAO自定义属性转换器PropertyConverter

java实现多表的自定义查询。

在 ScalaQuery O/R 框架中映射自定义类型

如何为具有映射到多个柴油列的自定义字段的类型派生 Queryable?

如何将一个自定义实体映射到实体框架中的某些数据库表?

ActiveRecord 自定义 SQL 结果自动映射