快速的基于磁盘的哈希表?

时间:2009-01-30 11:04:47

标签: hashtable

我有一组哈希(MD5的前64位,所以它们是非常随机分布的)我希望能够看到一个新的哈希是否在一个集合中,并将它添加到一个集合中。

集合不是太大,最大的将是数百万个元素,但是有数百个集合,所以我无法将它们全部保存在内存中。

到目前为止我的一些想法:

  • 我尝试将它全部保存在sqlite表中,但一旦它无法适应内存中的所有内容,它就变得非常慢。
  • Bloom过滤器听起来像错误率非常高。我不介意微小的错误率(64位散列已经在4G元素集上发生了1次冲突),但错误率如1%则太高了。
  • 保持文件中具有间隙的哈希的排序列表,并在没有足够的间隙时调整大小。哈希是均匀分布的,所以即使非常简单的方案也应该有效。

我错过了一些非常明显的东西吗?任何提示如何实现良好的基于​​磁盘的哈希表?

6 个答案:

答案 0 :(得分:13)

这是我最终使用的解决方案:

  • 每套一个文件
  • 文件包含2 ^ k个桶,每个256个字节或32个8个字节的条目
  • 空条目刚被清零(000 ...是一个有效的哈希值,但我并不关心2 ^ -64碰撞的可能性,如果一切都可以与哈希的本质相冲突)。
  • 每个哈希都驻留在通过其前k位猜测的桶中
  • 如果任何存储桶溢出,请将文件大小翻倍并拆分每个存储桶
  • 一切都是通过mmap()访问的,而不是read()/ write()

它比sqlite快得令人难以置信,即使它是低级别的Perl代码,Perl实际上并不适用于高性能数据库。它不适用于任何比MD5分布更不均匀的东西,它假设一切都非常均匀,以保持实现简单。

我最初尝试使用seek()/ sysread()/ syswrite(),而且速度非常慢,mmap()版本的速度要快得多。

答案 1 :(得分:9)

我在描述您确切的问题/需求时遇到了一些麻烦,但它仍然让我想到了Git以及它如何在磁盘上存储SHA1引用:

获取给定哈希的十六进制字符串表示,例如“abfab0da6f4ebc23cb15e04ff500ed54”。在哈希中剪切两个第一个字符(在我们的例子中为“ab”)并将其放入目录中。然后,使用其余的(“fab0da6f4ebc23cb15e04ff500ed54”),创建文件,并将内容放入其中。

这样,您可以通过自动索引在磁盘上获得相当不错的性能(取决于您的FS)。此外,您可以直接访问任何已知的哈希,只需在两个第一个字符之后楔入目录分隔符(“./ab/fab0da [..]”)

如果我完全错过了球,我很抱歉,但运气好的话,这可能会给你一个想法。

答案 2 :(得分:6)

听起来像是Berkeley DB的工作。

答案 3 :(得分:3)

其他基于磁盘的散列算法/数据结构包括线性散列和可扩展散列。

答案 4 :(得分:1)

首先我想到了两种算法:

  • 使用b-tree
  • 通过使用哈希的前10位索引到1024个单独文件中的一个来分离哈希本身,每个文件都包含从这10个位开始的所有哈希的排序列表。这会让你有一个恒定时间跳转到一个应该适合内存的块,并在你加载那个块后进行log(n)搜索。 (或者你可以使用8位来散列成256个文件等)。

答案 5 :(得分:0)

因为对于哈希你必须使用随机访问,我怀疑任何数据库都会给你不错的性能。您最好的选择可能是增加光盘缓存(更多RAM),并获得具有非常高的随机访问速度的硬盘(可能是固态磁盘)。