搜索非常大的彩虹表文件

时间:2013-01-26 07:25:20

标签: mysql c file-io large-data

我正在寻找搜索非常大的彩虹表文件(13GB文件)的最佳方法。它是一个CSV样式的文件,看起来像这样:

1f129c42de5e4f043cbd88ff6360486f; somestring
78f640ec8bf82c0f9264c277eb714bcf; anotherstring
4ed312643e945ec4a5a1a18a7ccd6a70; yetanotherstring

...你明白了 - 大概有大约9亿行,总是带有散列,分号,明文字符串。

基本上,程序应该查看是否在此文件中引用了特定的哈希值。

最快的方法是什么? 显然,我无法将整个文件读入内存,然后在其上放置strstr()

那么最有效的方法是什么?

  1. 逐行读取文件,始终为strstr();
  2. 读取文件的较大块(例如10.000行),执行strstr()
  3. 或者更高效地将所有这些数据导入MySQL数据库,然后通过SQL查询搜索哈希值?

    感谢任何帮助

3 个答案:

答案 0 :(得分:2)

最好的方法是对其进行排序,然后在其上使用类似binary search的算法。对它进行排序后,需要大约O(log n)时间来查找特定条目,其中n是您拥有的条目数。您的算法可能如下所示:

  1. 保持起始偏移和结束偏移。将起始偏移量初始化为零,并将偏移量结束到文件大小。
  2. 如果start = end,则没有匹配。
  3. 从偏移量(开始+结束)/ 2中读取一些数据。
  4. 向前跳过,直至看到换行符。 (您可能需要阅读更多内容,但如果您选择适当的大小(大于您的大多数记录),请在步骤3中阅读,您可能不必再阅读了。)
    • 如果你所使用的哈希是你正在寻找的哈希,请继续执行第6步。
    • 否则,如果您所使用的哈希值小于您要查找的哈希值,请将start设置为当前位置并转到步骤2.
    • 如果您所使用的哈希值大于您要查找的哈希值,请将结束设置为当前位置并转到步骤2.
  5. 跳到分号和尾随空格。未散列的数据将从当前位置到下一个换行符。
  6. 这可以很容易地转换为带休息的while循环。

    使用适当的索引将其导入MySQL,这样会使用类似的(或更多,因为它可能包装得很好)高效的算法。

答案 1 :(得分:0)

当您将整个性能优化移动到数据库时,您最后的解决方案可能是最容易实现的(通常它们会针对此进行优化)。

strstr在搜索字符串时没用,但是你知道一种特定的格式,可以跳过并比较更多面向目标。关于strncmpstrchr

读取单行的开销非常高(因为文件IO通常就是这种情况)。因此,我建议您阅读更大的块并在该块上执行搜索。我甚至考虑通过读取另一个线程中的下一个块来并行搜索并在那里进行比较。

您还可以考虑使用内存映射IO而不是标准C文件API。使用此功能,您可以将整个内容加载到操作系统中,而不必关心自己的缓存。

当然,重组数据以便更快地访问也会对您有所帮助。例如,插入填充字节,因此所有数据集都相等。这将为您提供随机的"访问您的数据流,因为您可以轻松计算第n个条目的位置。

答案 2 :(得分:0)

我首先将单个大文件拆分为65536个较小的文件,因此如果哈希以0000开头,则它位于文件00/00data.txt中,如果哈希以0001开头它在文件00/01data.txt等中。如果完整文件是12 GiB,则每个较小的文件(平均)为208 KiB。

接下来,将哈希与字符串分开;这样你就有了65536个“哈希文件”和65536个“字符串文件”。每个哈希文件将包含哈希的剩余部分(仅最后12位,因为不再需要前4位)以及相应字符串文件中字符串的偏移量。这意味着(而不是平均每个208 KiB的65536个文件)你有65536个哈希文件,每个120 KiB,65536个字符串文件,每个可能100 KiB。

接下来,哈希文件应该是二进制格式。 12个十六进制数字的成本为48位(不是12 * 8 = 96位)。仅此一项就会使哈希文件的大小减半。如果字符串在字符串文件中的4字节边界上对齐,那么16位“字符串/ 4的偏移量”就可以了(只要字符串文件小于256 KiB)。哈希文件中的条目应按顺序排序,相应的字符串文件应按相同的顺序排列。

经过所有这些改变;您将使用散列的最高16位来查找正确的散列文件,加载散列文件并执行二进制搜索。然后(如果找到)你将从哈希文件中的条目获得字符串开头的偏移量(在字符串文件中),并从哈希文件中的下一个条目获取下一个字符串的偏移量。然后你将从字符串文件中加载数据,从正确字符串的开头开始,到下一个字符串的开头结束。

最后,您将在内存中实现“哈希文件缓存”。如果您的应用程序可以分配1.5 GiB的RAM,那么这足以缓存一半的哈希文件。在这种情况下(缓存的一半哈希文件)你会期望从磁盘加载的唯一一件事就是字符串本身(例如可能少于20个字节)和另一半时间你需要从磁盘加载需要首先将哈希文件加载到缓存中(例如60 KiB);所以平均每次查找你将从磁盘上加载大约30 KiB。当然,更多的记忆更好(而且越少越好);如果你可以分配超过大约3 GiB的RAM,你可以缓存所有的哈希文件并开始考虑缓存一些字符串。

更快的方法是使用可逆编码,这样您就可以将字符串转换为整数,然后将整数转换回原始字符串,而不进行任何类型的查找。举个例子;如果所有字符串都使用小写ASCII字母并且是最大值。如果长度为13个字符,那么它们都可以被转换为64位整数并返回(如26 ^ 13 <2 ^ 63)。这可能导致不同的方法 - 例如在可能的情况下使用可逆编码(整数/散列的位64清除);并且只对可以以可逆方式编码的字符串使用某种查找(对于整数/散列集的位64)。通过一点点知识(例如仔细选择字符串的最佳可逆编码),这可能会将13 GiB文件的大小缩小到“足够小以容易放入RAM”,并且速度提高了很多个数量级。