例如,您有一个应用程序来处理由不同客户端发送的文件。客户端每天发送大量文件,并将这些文件的内容加载到您的系统中。文件格式相同。给出的唯一约束是不允许两次运行相同的文件。
为了检查您是否运行了特定文件,要创建文件的校验和并将其存储在另一个文件中。因此,当您获得新文件时,您可以创建该文件的校验和,并与您运行和存储的其他文件的校验和进行比较。
现在,包含您到目前为止运行的所有文件的所有校验和的文件变得非常非常庞大。搜索和比较花费了太多时间。用户在点击按钮时会发疯,并且几秒钟内看不到服务器的响应。
注意:应用程序使用平面文件作为其数据库。请不要建议使用rdbms等。目前根本不可能。
您认为可以采用其他方式检查重复文件吗?
答案 0 :(得分:4)
将它们保存在不同的位置:有一个目录客户端上传文件进行处理,另一个目录存放这些文件。
或者您是否处于客户端可以多次上传同一文件的情况?如果是这样的话,那么你每次都必须做一个完整的比较。
校验和,虽然它们让你相信两个文件不同(并且,根据校验和,非常高的置信度),但不是100%保证。你根本无法获得几乎无限的可能的多字节流,并将它们减少到32字节的校验和,并保证唯一性。
另外:考虑分层目录结构。例如,将使用路径foobar.txt
存储文件/f/fo/foobar.txt
。这将最大限度地降低扫描特定文件的目录(线性操作)的成本。
如果您保留校验和,则可以将其用于分层:/1/21/321/myfile.txt
(使用结构的最低有效数字;本例中的校验和可能是87654321)。
答案 1 :(得分:3)
不。您需要比较所有文件。严格来说,需要将每个新文件的内容与所有已经看过的文件进行比较。您可以使用校验和或散列函数对其进行近似,但是如果您找到索引中已列出的新文件,那么您需要进行完整的比较以确保,因为散列和校验和可能会发生冲突。
因此,它归结为如何更有效地存储文件。
我建议您将其留给专业软件,例如berkleydb或memcached或voldemort等。
如果你必须自己动手,你可以看看二元搜索背后的原则(qsort,bsearch等)。
如果您按照排序的形式维护已见校验和的列表(以及完整文件的路径,对于我上面提到的那个复选),您可以使用二进制搜索来搜索它。但是,以正确的顺序插入每个新项目的成本变得越来越昂贵。
对大量哈希的一种缓解就是对哈希进行二重分类,例如有256个二进制位对应于哈希的第一个字节。您显然只需要在以该字节代码开头的哈希列表中进行搜索和插入,并省略存储中的第一个字节。
如果您管理着数以亿计的哈希值(在每个bin中),那么您可以考虑进行两阶段排序,以便为每个哈希创建一个主列表,然后是“最近”列表;一旦最近的列表达到某个阈值,比如100000个项目,那么你将合并到主列表(O(n))并重置最近的列表。
答案 2 :(得分:2)
您需要将所有新文档与之前的所有文档进行比较,有效的方法是使用哈希值。
但是您不必将所有哈希存储在单个无序列表中,下一步也不必是完整数据库。相反,您可以根据第一个数字或散列的2位数,然后基于接下来的2位数的文件以及包含散列的散列列表的文件来创建目录。 (或任何类似的方案 - 你甚至可以使它自适应,当文件变得太大时增加级别)
这样搜索匹配涉及到几个目录查找,然后是文件中的二进制搜索。
如果您获得了大量快速重复(同时提交相同的文件),那么旁观缓存也可能值得拥有。
答案 3 :(得分:0)
如果我理解你的情况和要求,我认为你将不得不重新设计系统。
为了澄清,我的工作基于客户端全天发送文件,我们可以认为文件名无关紧要,当您收到文件时,您需要确保其[i]内容[/ i ]与其他文件的内容不同。
在这种情况下,您需要将每个文件与每个其他文件进行比较。这不是真的可以避免的,而且你正在做的是你现在可以管理的最好的东西。 至少,要求一种避免校验和的方法是提出错误的问题 - 您 将传入的文件与今天已处理的整个文件集进行比较,并且比较校验和将比比较整个文件体快得多(更不用说后者的内存要求了......)。
但是,也许你可以加快检查速度。如果将已经处理的校验和存储在trie之类的内容中,那么查看给定文件(而不是校验和)是否已经处理起来要快得多。对于32个字符的哈希,您需要最多执行32次查找,以查看该文件是否已被处理,而不是与可能的每个其他文件进行比较。它实际上是对现有校验和的二进制搜索,而不是线性搜索。
答案 4 :(得分:0)
你至少应该将校验和文件移动到一个合适的数据库文件中(假设它还没有) - 尽管SQLExpress的4GB限制可能还不够。然后,与每个校验和一起存储接收的文件名,文件大小和日期,将索引添加到文件大小和校验和,并仅针对具有相同大小的文件的校验和运行查询。 但正如威尔所说,无论如何都无法保证检查重复的方法。
答案 5 :(得分:0)
尽管你要求不要求和RDBMS,我仍然会建议SQLite - 如果你将所有校验和存储在一个表中,索引搜索速度非常快,并且集成SQLite根本不是问题。
答案 6 :(得分:0)
正如Will在他较长的回答中指出的那样,你不应该将所有哈希存储在一个大文件中,而只是将它们分成几个文件。
假设字母数字格式的哈希是pIqxc9WI
。您将该哈希存储在名为pI_hashes.db
的文件中(基于前两个字符)。
当有新文件进入时,计算哈希值,取前2个字符,然后只在 CHARS_hashes.db 文件中进行查找
答案 7 :(得分:0)
创建校验和后,创建一个以校验和作为名称的目录,然后将文件放在那里。如果那里已有文件,请将您的新文件与现有文件进行比较。
这样,你只需要检查一个(或几个)文件。
我还建议在文件中添加一个标题(单行),它解释了里面的内容:创建日期,客户端的IP地址,一些业务键。应该选择标题,以便您可以检测重复项是否正在读取此单行。
[编辑]当你有一个包含许多条目的目录时,某些文件系统陷入困境(在这种情况下:校验和目录)。如果这是您的问题,请使用校验和的前两个字符作为父目录的名称来创建第二个层。根据需要重复。
不要切断下一级别的两个角色;通过这种方式,如果出现问题而无需手动切割校验和,您可以通过校验和轻松查找文件。
答案 8 :(得分:0)
正如其他人所说,拥有不同的数据结构来存储校验和是正确的方法。无论如何,虽然你已经提到你不想采用RDBMS方式,为什么不试试sqlite?你可以像文件一样使用它,它很快。它使用起来也非常简单 - 大多数语言也都内置了sqlite支持。在python中你需要不到40行代码。