代码改变世界

分布式文件快速搜索V7.0(多计算机并行)

2010-07-13 20:19 灵感之源 阅读(...) 评论(...) 编辑 收藏

 

系列文章

1.分布式文件快速搜索V7.0(多计算机并行/多种算法)

2.分布式文件快速搜索的设计与实现(开源/分布式计算/并行)

3.分布式文件快速搜索-技术细节分析(开源/并行)

 

 

 

前言

这个话题很古老了,用C#实现的也很多,很明显我是在造轮子了。不过我今晚闲得头疼,在codeplex碰见看见一个项目,就是找重复文件的,项目代码我没看,我只是想找事情做。对开发人员来说,舒展一下手指的最佳办法是敲代码了。我最喜欢通过写代码来学东西了,看着枯燥的书本就没劲。

 

需求

找重复文件,首先是要获取文件的特征,很久以前用CRC,虽然最近MD5已经给中国某大学研究员验证不可靠,不过我还是喜欢MD5:)。你可以选择SHA1,换个类名就可以了。

 

实现

实现的办法很简单,就是把所有文件的特征都算出来,然后比较。

 

问题是,如果盲目地对每个文件都先获取哈希特征,再进行比较,那会很浪费时间,譬如你的磁盘没有重复文件,或者有些很巨大的不重复文件,那你就要把绝大部分时间浪费在无谓的哈希获取上了。

 

所以,我们先使用文件大小进行碰撞,再对相同大小的进行碰撞,可以极大地提高效率。为什么选择文件大小呢?因为如果文件大小不一样,那就不可能内容重复了吧?为什么不同时选择文件的修改时间?文件修改时间不一样,文件内容也是可以一样的,对不?既然我们的目的只是找内容相同的文件(不是文件名相同),那我们就可以无视文件名和文件修改时间了。允许选择不同的特征组合,譬如先是单纯的文件大小,或者文件大小+文件名+创建时间。

 

实现过程是用Dictionary来根据特征分组,碰撞(相同文件内容)就放到该特征下的重复文件列表,最后找出所有重复的文件。

 

程序很简单,主要是原始文件和重复文件的选择。函数可以指定重复目录,如果匹配到是在重复目录下的,就认为是可删除的(相对保留原始文件而言,本程序没有删除代码。。。)重复的文件,如果没有指定重复目录,则自动选择第一个文件为原始文件。

 

并行的实现

 

一直想用多线程来实现提速,今晚闲得头疼,就简陋地实现。大概实现步骤:因为不是.NET3.5/4,所以没有PPL,只能模拟并行了:C# Parallelism: Executing Methods in Parallel in .NET 3.5 。原理是封装了ThreadPool,结合ManualResetEvent和WaitHandle.WaitAll等待所有线程完成。在.NET 2.0中,Dictionary不是线程安全的,又不想花时间自己写封装,就只有在网上找了个封装好的线程安全的Dictionary。多线程的思想是:分配任务,分而治之。业务逻辑是自行切割要检索的文件夹到不同的线程,得到结果后,最终实现并行计算。

 

网上发现的最quick & dirty的代码

http://www.hosca.com/blog/post/2010/04/13/using-LINQ-to-detect-and-remove-duplicate-files.aspx

 

这个代码极大地让感到我今晚写的代码很冗余。。。它用了几个Linq的语句:Select所有文件,GroupBy哈希值,然后ForEach删除。。。Linq是很精妙。。。不过这个办法跟我的老慢速算法是大同小异的,所以速度也是可以无视的。

 

 

我实现的代码(让你容易找点......)

 

老规矩,vs2005+c#2.0,quick & dirty,没linq,没var,没有自动属性。不要跟代码规范较真,也不要纠缠为什么代码都放一个文件,纯粹是为了方便粘贴到博客而已,在产品开发中,6000多个手工写的类我都是很有组织地归类的:)

 

代码中有7个算法:

WorkV1:最原始的,对每个文件都进行哈希值比对;

WorkV2:较新的算法,先用文件大小(或加上文件名、创建日期、修改日期等)进行过滤,匹配的文件组合才进行哈希值比对;

WorkV3:新的多线程算法,把多线程应用到文件检索和文件哈希值获取;

WorkV4:测试发现如果把多线程应用到文件读取,则性能急剧下降,但仅获取文件信息,则有很大提升,虽然瓶颈在文件读取,但起码在检索信息上,比起V2有了提升;

WorkV5:新的算法,文件检索使用多线程,文件哈希值获取则动态判断文件所在地物理磁盘,如果不是同一个物理磁盘,则是使用多线程,否则采用跟V2一样的算法;因为判断物理磁盘引入了System.Management.dll,使用了DriveIdentification条件编译;

WorkV6:新的算法,支持对互联网上的计算机进行搜索,哈希值的快速存取,文件内容搜索,全文索引;

WorkV7:最新的算法,添加了对协议提供者模式的支持,可以自由添加各种访问协议;

 

在这里比较性能其实意义不大,以为不同机器比较不同的文件,差异太大了,譬如你的机器有2个文件,一个巨大,一个渺小,用新的快速算法,微秒间完成,用老的慢算法,则你可能要去睡一觉。 

 

程序有3个条件编译:DRIVEID启用物理磁盘识别(需要引用System.Management.dll),VERBOSE启用详细的调试信息,LoG启用保存日志

 

使用

看代码,判断支持多个文件夹。先Find出重复文件组合(指纹/相同文件列表),再用FindAll找出可以删除的所有重复文件。


如果要实现分布式搜索,必须在被搜索的计算机运行WorkV7Manager的实例,具体参看代码。

 

本地搜索

 

搜索本地完全一样的文件
string duplicateFolder = @"d:\backup";
Dictionary
<string, MatchFileItem> result;
BaseWork worker 
= new WorkV6();
result 
= worker.Find(new FileURI[] { new FileURI(@"c:\download\"),  new FileURI(duplicateFolder)}
           , 
new string[] { }, "", SearchTypes.Size | SearchTypes.Name, MatchTypes.ContentSame);
List
<string> duplicatedFiles = worker.FindAll(result, duplicateFolder);

 


分布式搜索

 

初始化

//first initialize some settings
WorkUtils.Initialiaze(new KeyValuePair<stringstring>(), CompressionMethods.GZip, new System.Net.WebProxy());

 

 

服务器端

 

服务器端
BaseManager manager = new WorkV6HTTPManager();//you can use WorkV6TCPManager
Dictionary<string, UserAccess> users = new Dictionary<string, UserAccess>();
users.Add(
"user"new UserAccess("user""pass", UserRights.Discover | UserRights.Search));
string[] allowedPaths = new string[] { @"e:\temp\New Folder" };
manager.Start(users, allowedPaths, 
8880);

 

 

客户端

 

客户端
FileURI remoteFolder = new FileURI(@"202.2.3.4:8880/e:\temp\New Folder""user""pass"0, ObjectTypes.RemoteURI);
Dictionary
<string, MatchFileItem> result;
result 
= Worker.Find(new FileURI[] { new FileURI(@"c:\download\"),  remoteFolder }
            , 
new string[] { }, "YOUR KEYWORD HERE", SearchTypes.Size, MatchTypes.ContentExtract);
List
<string> matchFiles = Worker.FindAll(result, string.Empty);

 

 

 

已知问题

本程序并没有考虑因为文件量巨大而会造成内存不足等问题,这个就留到以后我闲得更头痛的时候再考虑吧。

 

如果你要同时测试多种算法,那是不可能的,因为所有算法的瓶颈是对文件的哈希值获取,而这个方法允许Windows对已经访问的文件进行缓存和预取(pre-fetch),这样当你测试完第一个算法,再用第二个算法的时候,就会发现秒级完成。所以你每测试出一个结果,就应该重启电脑。。。

 

未知问题

如果你发现有什么bug,麻烦告诉我,我喜欢学习:)

 

改进

1.2010-7-16 v1;

2.2010-7-14 v2 添加了对选择文件名/修改时间/创建时间为重复标准的支持;重构代码,引入了基类,方便测试;

3.2010-7-14 v2.1 再次重构,并添加了对指定文件类型的支持,允许正则表达式;

4.2010-7-15 v3 增加了对多线程的支持,速度提升;

5.2010-7-16 v3.1 修正了从v2开始出现的重复测试问题;

6.2010-7-16 v4 仅应用多线程于文件查找;

7.2010-7-16 v5 动态支持多磁盘多线程提速; 

8.2010-7-16 v5.1 增加了对使用文件属性过滤的支持,并重构了部分代码,并修正了动态多磁盘的判断;

9.2010-7-19 v5.2 允许单纯地查找匹配大小/文件名而不计算哈希值,增加了对文件名过滤的支持,并重构了部分代码,加入了对动态多磁盘的容错;

10.2010-7-20 v6 实现了网格搜索

10.2010-7-20 v6.1 添加了对文件发现的支持,如果远程计算机没有发布可访问的目录,则不进行搜索

10.2010-7-20 v6.2 添加了对删除重复文件的支持,并改造了用户访问机制 

11.2010-7-21 v6.3 添加了对快速存取哈希值的支持;

12.2010-7-21 v6.4 修正了快速存取哈希值的异常问题;

13.2010-7-23 v6.6 添加了对HTTP协议、内容匹配、全文索引的支持;

14.2010-7-26 v6.7 添加了对HTTP、TCP压缩传输的支持;

15.2010-7-28 v6.8 添加了NTFS USN Journal技术,检索文件信息速度提升10倍,源代码:http://filio.codeplex.com/SourceControl/changeset/changes/74232

16.2010-7-31 v6.9 添加了对基于角色的访问控制(RBAC)的权限管理;

17.2010-8-11 v7.0 添加了对协议提供者模式的支持;

 

TODO

1.目前来看,改进速度的办法是多线程了,多个线程获取目录文件,然后多个线程对文件列表进行哈希获取。 在V3中添加了对多线程的支持;

2.添加对分布式(互联网上的机器)的支持;在v6中添加了对网格搜索的支持;

3.添加对删除文件的支持; 在v6.1中添加了对删除文件的支持;

4.文件内容哈希值存储,以便以后快速获取(同时记录文件名、大小、修改时间和哈希值,任一不匹配则认为文件改变了)在v6.3中实现了哈希值快速存取

5.添加对HTTP协议的支持;在v6.6中实现了对HTTP协议的支持

6.添加对搜索文件内容的支持:完全匹配、全文索引;在v6.6中添加了对文件内容匹配、全文索引的支持;

7.添加对lucene.net和hubbledotnet的支持; 

8.添加对缓存的压缩支持;在V6.6添加了对缓存压缩的支持;

9.添加对网络传输(HTTP/TCP)的压缩支持;在v6.7添加了对网络压缩传输的支持;

10.添加对文件同步的支持:chunck/index/count,断点续传

11.添加对基于角色的访问控制(RBAC);在v6.9中添加了对其的支持;

12.如果检索互联网上的计算机,如果指定搜索目录存在大量的文件,则返回结果可能会因为太大而造成网络超时等问题;

13.添加对HTTP/TCP的SSL连接的支持;

14.添加对NTFS的USNJournal支持,可以比普通的检索文件信息(Directory.GetFiles)快10倍。。。 在v6.8中添加了对其的支持

 

 

代码下载

点击这里下载:Filio.zip

 

 

项目地址

本项目已经在http://filio.codeplex.com/ 开源