2

例如,您有一个应用程序处理由不同客户端发送的文件。客户端每天发送大量文件,您将这些文件的内容加载到您的系统中。这些文件具有相同的格式。给你的唯一限制是你不能运行同一个文件两次。

为了检查您是否运行了特定文件,请创建文件的校验和并将其存储在另一个文件中。因此,当您获得一个新文件时,您可以创建该文件的校验和,并与您运行和存储的其他文件的校验和进行比较。

现在,包含您迄今为止运行的所有文件的所有校验和的文件变得非常非常大。搜索和比较花费了太多时间。

注意:该应用程序使用平面文件作为其数据库。请不要建议使用 rdbms 之类的。目前根本不可能。

您认为还有其他方法可以检查重复文件吗?

4

9 回答 9

5

将它们保存在不同的地方:有一个目录供客户端上传文件进行处理,有另一个目录存储这些文件。

或者您是否处于客户端可以多次上传同一个文件的情况?如果是这种情况,那么您几乎每次都必须进行全面比较。

校验和虽然让您确信两个文件是不同的(并且,取决于校验和,非常高的置信度),但并不是 100% 保证的。您根本无法将可能的多字节流的几乎无限宇宙减少到 32 字节校验和,并保证唯一性。

另外:考虑分层目录结构。例如,文件foobar.txt将使用 path 存储/f/fo/foobar.txt。这将最大限度地降低扫描特定文件的目录(线性操作)的成本。

如果您保留校验和,这可以用于您的分层:(/1/21/321/myfile.txt使用结构的最低有效数字;在这种情况下校验和可能是 87654321)。

于 2009-11-09T12:26:51.280 回答
3

没有。您需要比较所有文件。严格来说,需要将每个新文件的内容与所有已经看到的文件进行比较。您可以使用校验和或哈希函数来近似此值,但如果您发现索引中已经列出了一个新文件,那么您需要进行完整比较以确定,因为哈希和校验和可能会发生冲突。

因此,它归结为如何更有效地存储文件。

我建议您将其留给专业软件,例如berkleydbmemcachedvoldemort等。

如果你必须自己动手,你可以看看二进制搜索背后的原理(qsortbsearch等)。

如果您以排序形式维护所见校验和的列表(以及完整文件的路径,用于我上面提到的双重检查),您可以使用二进制搜索来搜索它。然而,以正确的顺序插入每个新项目的成本变得越来越昂贵。

对大量散列的一种缓解方法是对散列进行分箱排序,例如有 256 个分箱对应于散列的第一个字节。您显然只需要搜索并插入以该字节码开头的哈希列表,然后从存储中省略第一个字节。

如果您正在管理数亿个哈希(在每个 bin 中),那么您可能会考虑两阶段排序,这样您就有一个用于每个哈希的主列表,然后是一个“最近”列表;一旦最近的列表达到某个阈值,比如 100000 个项目,那么您将合并到主列表 (O(n)) 并重置最近的列表。

于 2009-11-09T12:28:30.443 回答
2

您需要将任何新文档与所有以前的文档进行比较,最有效的方法是使用哈希。

但是您不必将所有哈希值存储在一个无序列表中,下一步也不必是一个完整的数据库。相反,您可以拥有基于第一个数字或 2 位哈希值的目录,然后是基于接下来的 2 位数字的文件,以及那些包含已排序哈希列表的文件。(或任何类似的方案 - 你甚至可以使其自适应,当文件变得太大时增加级别)

这种搜索匹配的方式涉及到几个目录查找,然后是文件中的二进制搜索。

如果您获得大量快速重复(同时提交相同的文件),那么后备缓存也可能值得拥有。

于 2009-11-09T12:36:02.893 回答
0

如果我正确理解您的情况和要求,我认为您将不得不重新设计系统。

澄清一下,我的工作是基于客户全天向您发送文件,我们可以假设文件名无关紧要,当您收到文件时,您需要确保其 [i] 内容 [/i] 不是与另一个文件的内容相同。

在这种情况下,您确实需要将每个文件与其他每个文件进行比较。这并不是真正可以避免的,而且您目前正在尽力而为。 至少,要求一种避免校验和的方法是在问错误的问题——您必须将传入的文件与今天已经处理的整个文件语料库进行比较,并且比较校验和将比比较整个文件快得多身体(更不用说后者的内存要求......)。

但是,也许您可​​以加快检查速度。如果您将已经处理的校验和存储在类似trie的东西中,那么查看给定文件(而不是校验和)是否已经被处理应该会快得多。对于 32 个字符的哈希,您最多需要进行 32 次查找以查看该文件是否已被处理,而不是与可能的所有其他文件进行比较。它实际上是对现有校验和的二进制搜索,而不是线性搜索。

于 2009-11-09T12:35:06.447 回答
0

您至少应该将校验和文件移动到适当的数据库文件中(假设它还没有) - 尽管 SQLExpress 的 4GB 限制在这里可能还不够。然后,与每个校验和一起存储文件名、文件大小和接收日期,为文件大小和校验和添加索引,并仅针对具有相同大小的文件的校验和运行查询。但正如威尔所说,无论如何都不能保证您检查重复项的方法。

于 2009-11-09T12:44:15.960 回答
0

尽管您要求不要建议和 RDBMS,但我仍然会建议SQLite - 如果您将所有校验和存储在一个带有索引的表中,搜索将非常快,并且集成 SQLite 根本不是问题。

于 2009-11-09T12:47:47.190 回答
0

正如威尔在他更长的回答中指出的那样,您不应该将所有哈希值存储在一个大文件中,而应将它们简单地分成几个文件。

假设字母数字格式的哈希是pIqxc9WI. 您将该哈希存储在一个名为pI_hashes.db(基于前两个字符)的文件中。

当一个新文件进来时,计算hash,取前2个字符,只在CHARS_hashes.db文件中查找

于 2009-11-09T12:51:36.867 回答
0

创建校验和后,创建一个以校验和为名称的目录,然后将文件放在那里。如果那里已经有文件,请将您的新文件与现有文件进行比较。

这样,您只需检查一个(或几个)文件。

我还建议在文件中添加一个标题(单行)来解释里面的内容:它的创建日期、客户端的 IP 地址、一些业务密钥。应该以这样一种方式选择标题,以便您可以检测到重复读取这一行。

[编辑] 当您有一个包含许多条目的目录(在这种情况下:校验和目录)时,某些文件系统会陷入困境。如果这对您来说是个问题,请使用校验和的前两个字符作为父目录的名称来创建第二层。根据需要重复。

不要从下一级切断两个字符;这样,如果出现问题,您可以通过校验和轻松找到文件,而无需手动剪切校验和。

于 2009-11-09T13:07:51.327 回答
0

正如其他人所提到的,使用不同的数据结构来存储校验和是正确的方法。无论如何,虽然你已经提到你不想走 RDBMS 的方式,为什么不试试 sqlite 呢?您可以像使用文件一样使用它,而且速度非常快。它使用起来也非常简单——大多数语言也内置了 sqlite 支持。在说 python 中,它只需要不到 40 行代码。

于 2009-11-09T13:27:06.527 回答