Spiga

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

2010-07-26 18:37 by 灵感之源, 3210 visits, 收藏, 编辑

 

系列文章

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

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

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

 

 

 

前言

 在上一篇文章中,对分布式文件快速搜索的设计与实现进行了说明。今天,将对具体的实现细节进行分析。

 

文件的检索

 

  • 文件获取

    1). 一般地,用Directory.GetDirectories()加上SearchOption.AllDirectories来获取某个的目录下的所有文件(包括任意层子文件)。但在这里会采用自行递归获取,每获取一层目录的文件,都会预先根据SearchTypes条件(如文件大小、文件名、修改时间、属性等)来碰撞。具体参看WorkV7FindFileRunner.FindLocal方法。

    2). 相比递归获取文件列表,我们可以直接读取物理磁盘的MFT(Master File Table)主文件表,这样不需要逐个目录递归,直接顺序获取所有文件夹和文件记录,速度可以比递归快10倍以上。在Windows NT便引入的NTFS文件系统,拥有USN Journal(文件日志),每个文件的变化(添加、修改、删除、更名等)都会被记录,在通过MFT获取所有文件后,每次运行,只需要判断一下系统的变化并记录便可,无需重新扫描整个磁盘。

 

  • 哈希获取

    在使用SearchTypes条件过滤完成后,使用MD5CryptoServiceProvider来获取MD5哈希值。当然,你可以使用SHA1CryptoServiceProvider计算哈希值,如果你觉得MD5不可靠,具体参看WorkUtils.HashMD5File。


    实际的哈希获取,会使用缓存。缓存的实现使用快速二进制序列化,原理是判断缓存数据库是否存在一样的文件信息(文件名、大小和修改时间),如果匹配,则返回已经存在的哈希值,否则就获取新的哈希值。具体参看LocalHashStorage。

 

文件的匹配/比较

 文件的匹配有3种方式:完全一致、包含以及全文索引。

  1. 完全一致

    完全一致,直接使用哈希值比较。

     

  2. 包含

    适用于运行时搜索,判断文本文件中包含的内容。目前直接使用File.ReadAllText(file).IndexOf(keyword, StringComparison.InvariantCultureIgnoreCase)来判断,可能遇到的问题应该是文件太大导致内存溢出。

     

  3. 全文索引

    可索引文件的全文内容会自动缓存,支持自定义扩展接口IFileContentIndex,目前内置了微软的IFilter实现。具体参看LocalFileContentIndexStorage。

 

并行计算的处理

 

实现

因为不是.NET3.5/4,没有PPL,只能模拟并行,来源参看分布式文件快速搜索v7.0(多计算机并行/多种算法)。原理是使用ThreadPool.QueueUserWorkItem各个任务,使用ManualResetEvent记住每个任务的状态,并用WaitHandle.WaitAll等待所有任务完成。具体参看ParallelProcessor.ExecuteParallel。

 

 

ExecuteParallel
public static void ExecuteParallel(IParallelWorker[] methods)
        {
            
if (methods.Length > 0)
            {
                
// Initialize the reset events to keep track of completed threads
                ManualResetEvent[] resetEvents = new ManualResetEvent[methods.Length];

                
// Launch each method in it's own thread
                for (int i = 0; i < methods.Length; i++)
                {
                    resetEvents[i] 
= new ManualResetEvent(false);
                    ThreadPool.QueueUserWorkItem(
new WaitCallback(delegate(object index)
                    {
                        
int methodIndex = (int)index;

                        
// Execute the method
                        methods[methodIndex].Run();

                        
// Tell the calling thread that we're done
                        resetEvents[methodIndex].Set();
                    }), i);
                }

                
// Wait for all threads to execute
                WaitHandle.WaitAll(resetEvents);
            }
        }

 

 

线程安全

在多线程的环境中,最常见的问题是线程安全。.NET 2.0中,Dictionary是不安全的,我用网上封装的SynchronizedDictionary,当然,我们还可以使用折腾箱子的HashTable。在.NET 4.0中,你可以使用ConcurrentDictionary。

 

除了集合,在访问FileStream等对象的情况,你都必须注意保证线程安全,否则数据会跟实际预想不一致。

 

任务切分

并行计算最关键是如何切分任务。在上一篇文章中我已经简略地说明,实际的逻辑比较复杂。首先,根据要搜索的文件组和最大任务数进行切分,在每个任务中,判断文件夹是否为远程文件夹,如果是,则使用分布式搜索,否则本地搜索,具体参看WorkV7FindFileRunner。

 

在上述所有文件夹搜索任务都完成后,聚合搜索结果,再根据网络/本地切分任务。对于本地文件夹,则判断是否为同一个物理磁盘,如果是,则动态使用并行计算以实现加速。具体参看WorkV7.Find()。

 

具体参看上一篇文章的流程图、WorkV7FindFileRunner(第一次根据文件大小、名称等过滤)和WorkV7FindResultRunner(根据文件属性过滤后再根据匹配方式搜索)。

 

 

分布式的实现

这是本程序的最大特点。分布式的原理就是在各个节点部署一个监听程序,多个节点组合成一个grid,在监听的同时,也可以作为客户端发送请求。

 

数据的传输

本程序没有使用XML作为数据载体,而是使用了自定义的格式:文件头+数据体(如文件内容等),文件头包括了命令等信息。整个内容可选压缩算法,每个数据包在最后自动添加结束标记,以便在TCP中识别。

 

协议

分布式文件快速搜索自定义了协议接口,内置了对HTTP、TCP和FTP的支持,你可以自由添加各种协议。每种网络连接,都会使用异步处理,避免堵塞请求。

 

譬如:

 

c:\temp\abc.txt
\\lancomputer\temp\abc.txtt
http://1.2.3.4:88/foo/abc.txt
tcp://1.2.3.4:55/foo/abc.txt
ftp://1.2.3.4/foo/abc.txt
pop3://1.2.3.4:101/foo/abc.txt
skydrive://foo/abc.txt
dropbox://foo/abc.txt
AmazonS3://foo/abc.txt
flicker://foo/abc.jpg
facebook://foo/abc.jpg

 

  1. HTTP

    每个数据包都使用POST方式,在可选压缩后把数据写入Request.GetRequestStream,具体参看HTTPFileWorkProvider。在服务器端,使用HttpListener监听,在获得每个HttpListenerRequest后,调用基类(BaseManager)的ProcessRequest处理Request.InputStream,具体参看WorkV7HTTPManager

     

    HttpListener有点诡异,使用fooListener.Prefixes.Add()来定义监听的路径。在调用HttpListener之前,你需要使用HttpListener.IsSupported判断一下你的操作系统是否支持:必须XP SP2或以上、Win2003、Vista、08、Win7。HttpListener本身不支持SSL,但你可以httpcfg.exe来配置,之前我参看的是一篇英文的文章,现在一下子找不到,大家就凑合用中文的吧:配置HttpListener侦听SSL连接详解

     

  2. TCP

    每个具体的操作,会先使用AsynchronousClient进行连接,服务器使用AsynchronousSocketListener进行监听,在Received事件处理客户端发送来的请求,具体参看TCPFileWorkProvider和WorkV7TCPManager。

     

  3. FTP

    使用.NET内置的FTPWebRequest,具体参看FTPFileWorkProvider。

 

大数据的传输

在下一个大版本中,将会提供对文件同步的支持。文件传输,有很多现成的软件可以做参考。我的设想是:把文件动态切分成多个小块以减少内存的占用,标记之,成功就记录一个,失败/断开后下次传输,则可以断点续传。当然,一样可选压缩算法,以提高传输性能。

 

 

代码下载

点击这里下载:Filio.zip

 

 

项目地址

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

 

 

Add your comment

20 条回复

  1. #1楼[楼主] 灵感之源      2010-07-27 06:49
    本项目已经在http://filio.codeplex.com/ 开源
     回复 引用 查看   
  2. #2楼 囧月      2010-07-27 07:52
    不知在本机搜索上相比everything怎么样?
    everything也有http,etp/ftp功能,跟楼主的有些相似?就是api不好用……
     回复 引用 查看   
  3. #3楼[楼主] 灵感之源      2010-07-27 08:11
    @囧月
    everything 速度不错,它的做法是全盘实时搜索,它之所以这么快,是因为它使用了NTFS磁盘的USNJournal特性,所以它不能在非NTFS磁盘搜索。

    但是没发现它能检索文件内容,也没有寻找完全匹配文件的功能。

    目前Filio只是提供基本的接口,没有实现界面,如果有了界面,那可以在用户输入的时候实时搜索,也可以优先检索最近访问的文件夹和目录,并以事件实时返回每个匹配。
     回复 引用 查看   
  4. #4楼 深蓝医生      2010-07-27 10:15
    收藏,我们目前也在做分布式应用,不过是分布式查询相关的。
     回复 引用 查看   
  5. #5楼[楼主] 灵感之源      2010-07-27 10:57
    即将添加对NTFS的USNJournal支持,可以比普通的检索文件信息(Directory.GetFiles)快10倍。。。
     回复 引用 查看   
  6. #6楼 snow365      2010-07-27 11:51
    不错,很好的项目,代码写的很有水平
     回复 引用 查看   
  7. #7楼 囧月      2010-07-27 12:15
    @灵感之源
    原来如此,等楼主出了USNJournal一定要体验一下……
     回复 引用 查看   
  8. #8楼[楼主] 灵感之源      2010-07-27 13:50
    @囧月
    也会使用条件编译:NTFS,因为不想直接使用非.NET内置的
     回复 引用 查看   
  9. #9楼[楼主] 灵感之源      2010-07-27 18:15
    我的笔记本扫描有92GB内容的分区,13万4千文件,全部检索出来,2秒内,NTFS USNJournal的威力太强悍
     回复 引用 查看   
  10. #10楼[楼主] 灵感之源      2010-07-27 21:08
    c#版的sqlite性能真不是一般的差。。。100万条短记录插入,我的i5 2.4+2g+7200rpm,耗时3分24秒,读取耗时44秒。我的自定义二进制序列化,插入1秒,读取1秒
     回复 引用 查看   
  11. #11楼[楼主] 灵感之源      2010-07-28 18:30
    v6.8 添加了NTFS USN Journal技术,检索文件速度提升10倍,源代码:http://filio.codeplex.com/SourceControl/changeset/changes/74232
     回复 引用 查看   
  12. #12楼[楼主] 灵感之源      2010-07-28 18:37
    @囧月
    已经添加了对NTFS USN Journal的支持
     回复 引用 查看   
  13. #13楼 囧月      2010-07-29 09:35
    @灵感之源
    ntfs这个速度就是爽~
    有个问题就是,我的每个盘System Volume Information目录都只有SYSTEM用户,我必须手动加入Administrators组,否则无法访问usn journal,这个可否更改呢?
    那个一堆的pinvoke感觉很不爽吧。。。
    话说好多的预编译指令,外加每个文件起码都上千行的代码,看的好晕的哦~
     回复 引用 查看   
  14. #14楼[楼主] 灵感之源      2010-07-29 09:45
    @囧月
    1.NTFS特性必须用admin权限,自行解决

    2.不用pinvoke,你给我实现这个特性看看?

    3.设计初衷,没打算按每个类切分,一个小应用,过百个类也看着不爽
     回复 引用 查看   
  15. #15楼[楼主] 灵感之源      2010-07-29 10:11
    @囧月
    整理了一下,现在比较清晰了
     回复 引用 查看   
  16. #16楼 囧月      2010-07-29 10:16
    @灵感之源
    刚刚试了下,把System Volume Information的所有用户都删了,Everything还能用。。。

    对c++一窍不通的我来说,pinvoke是一种痛苦啊。。。所以坐等现成的用~:)
     回复 引用 查看   
  17. #17楼 传说中的小YY      2010-08-02 16:12
    ParallelProcessor类 对应 C#4.0 里的 Parallel
    而4.0里的Parallel 在多核情况下会更优。

    SynchronizedDictionary类 对应 C#4.0 里的System.Collections.Concurrent; 目的还是为了线程安全的问题



    =。= 所有类写在一个文件里 是不是看着眼花。
     回复 引用 查看   
  18. #18楼[楼主] 灵感之源      2010-08-02 16:33
    @传说中的小YY

    设计的初衷是为了方便粘贴代码到博客,所以所有代码都放到一个类中,现在慢慢地把不同的业务放到不同的类。

    这个也只是练手的工余项目,没注释,没按每个类独立文件
     回复 引用 查看   
  19. #19楼[楼主] 灵感之源      2010-08-02 17:57
    开始全面重构Filio-分布式文件管理,引入提供者模式,内置对HTTP/TCP的支持,增加对skydrive/azure/amazon s3/ftp/pop3/flickr/facebook/dropbox等的支持,目标是成为最强的开源分布式文件管理
     回复 引用 查看   
  20. #20楼 Treenew Lyn      2011-12-05 10:46
    不错。这个博客可以关注。
     回复 引用 查看