【问题标题】:Search for a string from 100 million rows of strings从一亿行字符串中搜索一个字符串
【发布时间】:2013-12-19 15:38:47
【问题描述】:

我有这个包含一些 md5 哈希值的文本文件,其中有 1 亿行。我有另一个包含几千个 md5 哈希的较小文件。我想从这个新的小文件到旧的大文件中找到这些 md5 哈希的对应索引。

最有效的方法是什么? 15分钟左右能搞定吗?

我尝试了很多东西,但它们都不起作用。首先,我尝试将较大的数据导入数据库文件并在 md5 哈希列上创建索引。创建这个哈希需要永远。我什至不确定这是否会大大提高查询速度。有什么建议吗?

【问题讨论】:

  • @user2864740 CREATE INDEX my_index ON table (sequence_hash) using HASH;??这需要很长时间
  • 没关系,它会的(哈希没有排序的事实也很痛苦;首先对输入文件进行排序可能会有所帮助!)。但是对它的查询会很快。
  • @user2864740。做一次查询需要多长时间。如果它需要一秒钟,那么它仍然是几个小时。我创建的索引是正确的吗?或者我必须做其他事情。我刚从某个地方得到那个例子,不确定它是否合适。 sequence_hash 是包含 md5 哈希的那个
  • 我的查询将只是从其中 sequence_hash='some_hash' 的表中选择 id
  • 您尝试使用哪种数据库?从您的用例来看,键值数据库应该适合您。或者,如果您需要经常这样做并且文件经常更改,您可以尝试一些轻量级的 mapreduce 框架来执行此操作。

标签: database algorithm


【解决方案1】:

不要在 db 中这样做 - 使用简单的程序。

  1. 将小文件中的 md5 哈希读入内存中的 哈希映射,以便快速查找。
  2. 然后逐行读取大文件中的 md5,并检查该行是否在 hash map 中。

hash map中的平均查找时间应该接近O(1),所以这个过程的时间基本上就是你有多快可以读取大文件。

用今天的硬件用这种方法很容易获得 15 分钟。

【讨论】:

    【解决方案2】:

    首先:100 Megarows à 32 Bytes = ca. 3.2 GB 数据。在 15 分钟内读取它们转换为每秒 3.5 兆字节,这对于现代硬件来说应该很容易实现。

    我建议不要使用数据库,而是包含一些简单步骤的过程:

    1. 对数据进行排序 - 您只需执行一次,即可并行处理大部分数据
    2. 将小文件读入内存(排序成数组)
    3. 循环这个数组:
    4. 逐行读取大文件,与数组的当前行进行比较(首先比较第一个字节,然后比较第一个和第二个,...)直到达到匹配(输出索引)或传递值(输出“未找到”)
    5. 移动到下一个数组元素

    初始排序可能很容易花费超过 15 分钟,但查找应该非常快:如果您有足够的 RAM(以及支持大于 2GB 的进程的操作系统),您应该能够获得至少快一个数量级!

    【讨论】:

    • 对初始数据进行排序实际上没有任何意义:虽然它可以让你在扫描完整复杂度时进行 O(1) 比较,但将是 N * log(N)(排序)+N(比较)。另一方面,简单的线性扫描将采用N * (cost of looking for the pattern in the smaller set of patterns),这意味着N * 1 带有哈希图,或者N * log(smaller number of patterns) 在较小的集合中使用二进制搜索。
    • 初始排序的中心点是将随机IO转换为顺序IO,在今天的硬件上可以轻松执行一个数量级的提升!
    • IO不需要随机;您可以按顺序读取文件,仍然可以有效地解决问题。
    • @Clément - 如果要与订单保证进行比较,要么需要排序,要么需要随机 IO。您在答案中提出的算法依赖于完整的比较(不需要排序保证),绝对会破坏任何 CPU 缓存使用并创建大量错误的分支预测。你最终会受到内存带宽的限制,这比顺序 IO 带宽更难(也更昂贵)扩展。用于大型比较集(=大于 CPU 缓存)的 Rabin-Karp 已过时,因为 CPU 性能和 RAM 带宽已经开始出现如此巨大的差异。
    • 因此,我关于使用集合的回答的第二部分简化了。最后很难说,但我真的怀疑对整个数据进行排序会比这里的任何其他解决方案更快。我实际上喜欢@Ebbe M. Pedersen 的回答。
    【解决方案3】:

    有专门设计用于在大文件中搜索多个字符串的算法。其中之一是拉宾-卡普。我有一个blog post about this。

    更简单地说,下面的伪代码应该很快就能让你到达那里:

    Load your few thousand strings in a set data structure
    For each line (index: i) in your file
        If that line appears in your set of values
            print i
    

    这将非常快:设置的数据结构将具有几乎即时的查找,因此 IO 将是罪魁祸首,15 分钟内可以容纳 1 亿个哈希和,没有太大难度。

    【讨论】:

      【解决方案4】:

      假设:

      (1)小文件中的每条记录都出现在大文件中

      (2)每个文件中的数据是随机排序的。

      选项:

      (1) 对于大文件中的每条记录,线性搜索小文件中的匹配项。由于大多数搜索都找不到匹配项,因此时间将接近 N 大 * N 小 * k 其中 k 表示尝试一次匹配的时间。

      (2) 对于小文件中的每条记录,线性搜索大文件中的匹配项。由于每次搜索都会找到匹配项,因此时间大约为 Nlarge/2 * Nsmall * k。

      这看起来是选项 (1) 的两倍——但前提是您可以将大文件完全放入快速内存中。您可能需要 6 GB 的 RAM。

      (3) 将小文件排序为易于搜索的形式。平衡二叉树是最好的,但排序数组几乎一样好。或者您可以相信一些方便的哈希表对象的作者在 CS 学校已经关注。对于大文件中的每条记录,在结构化小文件中搜索匹配项。时间将是 log2 Nsmall * s 对小文件进行排序,其中 s 表示对一条记录进行排序的时间,加上 log2 Nsmall * Nlarge * k 用于扫描。这给出了总时间 log2 Nsmall * (s + Nlarge * k)。

      (4) 将大文件排序为易于搜索的形式。对于小文件中的每条记录,在结构化大文件中搜索匹配项。时间将是 log2 Nlarge * s 对大文件进行排序加 log2 N 大 * N 小 * k 对于扫描,总共给出 log2 Nlarge * (s + Nsmall * k)。

      选项 (4) 显然是最快的,因为减小 Nlarge 的任何系数都将支配所有其他改进。但如果从大文件派生的可排序结构不能完全放入 RAM,那么选项 (3) 可能会更快。

      (5) 将大文件排序为易于搜索的形式。将此结构分解为适合您的 RAM 的部分。对于每个这样的片段,将片段加载到 RAM 中,然后对于小文件中的每条记录,搜索当前加载的片段以查找匹配项。时间将是 log2 Nlarge * s 对大文件进行排序加 log2 N 大 * N 小 * k * p 对于扫描,结构被分成 p 块,总共给出 log2 Nlarge * (s + Nsmall * k * p)。

      使用您为 Nlarge 和 Nsmall 指定的值,以及足够的 RAM 以使 p 可以保持为一位数,选项 (5) 似乎可能是最快的。

      【讨论】:

      • “排序一条记录的时间”真的没有意义,不是吗?选项 5 比选项 3 慢很多,因为选项 3 中的 s 为 Nsmall,而选项 5 中的 s 为 Nlarge。排序需要 N*log(N):你的 s 取决于样本大小。
      • 选项 5) 对 1 亿条记录进行排序似乎是一个非常糟糕的主意。将小文件读入哈希映射,然后在哈希映射中查找大文件中的每个条目,运行时间为 O(n)。几乎不可能做得更快。
      • 选项 3) 请解释为什么平衡二叉树比排序数组更好。我的经验是,对于搜索,排序数组更好,因为它更简单并且提供更好的参考位置。
      • 先生们,您是对的;我犯了错误。我错误地将排序时间定性为 O(log2(N)),而实际上它是 O(N*log2(N))。选项 (3) 更快。选项 (5) 是不必要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 2015-02-07
      • 2013-01-18
      • 1970-01-01
      相关资源
      最近更新 更多