通过网络验证3.5亿个文件的存在

时间:2015-12-05 11:51:47

标签: c# .net sql-server tsql

我有一个SQL Server表,大约有300,000,000个绝对UNC路径,我正在尝试(快速)验证每个路径,以确保SQL Server表中的路径实际上作为磁盘上的文件存在。

从表面上看,我正在以50,000的批量查询表格并递增计数器以便在我离开时推进我的批次。

然后,我正在使用数据读取器对象来存储我当前的批处理集并循环遍历批处理,使用File.Exists(path)命令检查每个文件,如下例所示。

问题是,我正在处理大约。四核3.4ghz i5最大每秒1000个文件,16GB内存,这需要几天时间。有更快的方法吗?

我在SQL Server表上有一个列存储索引,我已经对它进行了分析。我在< 1s中获得了50k的批量记录,因此在向.net控制台应用程序发布批次时,这不是SQL瓶颈。

while (counter <= MaxRowNum)
{
    command.CommandText = "SELECT id, dbname, location FROM table where ID BETWEEN " + counter + " AND " + (counter+50000).ToString();

    connection.Open();

    using (var reader = command.ExecuteReader())
    {
        var indexOfColumn1 = reader.GetOrdinal("ID");
        var indexOfColumn2 = reader.GetOrdinal("dbname");
        var indexOfColumn3 = reader.GetOrdinal("location");

        while (reader.Read())
        {
            var ID = reader.GetValue(indexOfColumn1);
            var DBName = reader.GetValue(indexOfColumn2);
            var Location = reader.GetValue(indexOfColumn3);

            if (!File.Exists(@Location.ToString()))
            {
                //log entry to logging table
            }
        }
    }

    // increment counter to grab next batch
    counter += 50000;

    // report on progress, I realize this might be off and should be incremented based on ID
    Console.WriteLine("Last Record Processed: " + counter.ToString());
    connection.Close();
}

Console.WriteLine("Done");
Console.Read();

编辑:添加一些其他信息:

考虑通过数据库本身完成所有这些工作;它是sql服务器企业,拥有2tb内存和64个内核。问题是sql server服务帐户无权访问托管数据的nas路径,所以我的cmdshell通过SP运行失败(我不控制AD的东西),UNC路径有成千上万的个别子基于文件的MD5哈希的目录。所以枚举目录的内容最终没有用,因为你可能有一个文件10个目录深只有1个文件。这就是我必须进行文字全路径匹配/检查的原因。

哦,一般来说道路很长。我实际上尝试将它们全部加载到内存中的列表,然后才意识到它相当于90gb的数据(lol,oops)。完全同意其他评论。数据库速度超快,完全没有担心。虽然不考虑SMB聊天,但很可能就是我正在遇到的问题。 - JRats 13小时前

哦!如果文件不存在,我也只更新数据库。如果确实如此,我不在乎。因此,我的数据库运行最小化以获取批量路径。基本上,我们将一堆数据从较慢的存储迁移到这个灵活的设备,并且我被要求确保通过编写一些内容来确认每个文件的存在。

线程帮助了很多。我跨越了4个线程的文件检查并使我的处理能力达到约3,300条记录/秒,这要好得多,但我仍然希望如果可以的话更快。有没有一个很好的方法来判断我是否受到SMB流量的约束?我注意到,一旦我试图将我的线程计数提高到4或5,我的速度就会下降到涓涓细流;我想也许我在某处陷入僵局,但没有。

哦,我无法对FilesOnNetwork进行检查,因为您说的确切原因,与我要检查的内容相比,实际托管的文件数量是3到4倍。这个灵活的设备可能有1.5b左右的文件。

6 个答案:

答案 0 :(得分:11)

优化SQL端在这里没有意义,因为你是文件IO绑定。

我会使用Directory.EnumerateFiles来获取所有存在文件的列表。枚举目录中的文件应该比单独测试每个文件快得多。

您甚至可以完全反转问题,并将该文件列表批量插入数据库临时表,以便您可以直接在数据库中执行基于SQL的集处理。

如果您想继续单独测试,您可能应该并行执行此操作。目前尚不清楚该进程是否真的受磁盘限制。可能是网络或CPU绑定。

通过重叠多个请求,并行性将有助于此。它是网络延迟,而不是可能成为问题的带宽。在DOP 1,至少一台机器在任何给定时间都处于空闲状态。有时候两者都是空闲的。

  与我要检查的内容相比,实际托管的文件数量是3倍或4倍

使用dir /b命令将所有文件名列表管道输入.txt文件。在具有文件的计算机上本地执行,但如果不可能远程执行。然后使用bcp将它们批量插入到数据库的表中。然后,您可以在单个SQL查询中执行快速存在检查,该查询将进行高度优化。您将获得哈希联接。

如果您希望并行化此策略的dir阶段,您可以为此编写程序。但也许没有必要,尽管是单线程的,但dir足够快。

答案 1 :(得分:10)

瓶颈最有可能是网络流量,或者更具体地说:SMB流量。您的计算机会与SMB通信以从网络存储中检索文件信息。中小企业的流量很快,您需要一些消息来检查文件的存在以及您是否有权阅读该文件。

对于它的价值,在我的网络上,我可以通过SMB查询每秒大约一百个文件的存在,而递归列出15K文件需要10秒。

可以更快的是预先检索远程目录列表。如果目录结构是可预测的,那么这将是微不足道的 - 如果存储在这些目录中不包含许多不相关的文件。

然后您的代码将如下所示:

HashSet<string> filesOnNetwork = new HashSet<string>(Directory.EnumerateFiles(
                            baseDirectory, "*.*", SearchOption.AllDirectories));

foreach (var fileToCheck in filesFromDatabase)
{
    fileToCheckExists = filesOnNetwork.Contains(fileToCheck);
}

如果网络上的文件数量超过您需要检查的数量,则可能会产生负面影响,因为filesOnNetwork的填充和搜索将成为您应用程序的瓶颈。

答案 2 :(得分:3)

在您当前的解决方案中获取50,000批次并打开和关闭连接没有任何目的,但会减慢速度。 DataReader流。只需打开一次,一次只读一遍。在封面下,Reader会一次发送批次。当您只阅读10时,DataReader不会尝试使用300,000,000行来阻塞客户端。

我认为您担心优化最快的步骤 - 从SQL读取

验证文件路径将是最慢的步骤

我喜欢CodeCaster的答案,但是有3.5亿人会使用.NET进入对象大小限制。通过读入HashSet,它不会开始工作,直到该步骤完成。

我会使用BlockingCollection和两个集合

  • 枚举文件
  • 写入db

最慢的步骤是读取文件名,以便尽可能快地中断并且不要中断。在设备上关闭到存储设备上执行此操作。在SAN连接设备上运行该程序。

我知道你会说写入db很慢但它只需要比枚举文件更快。只需要找到一个二进制列 - 不要将完整的文件名写入#temp。我会打赌美元到甜甜圈(优化)更新比枚举文件更快。将您的更新大量排列为10,000行,以保持往返行程。我会更新asynch,以便在当前处理时建立下一个更新。

然后最后检查数据库是否有任何未标记为已找到的文件。

不要先去中间收藏。直接处理枚举。这使您可以立即开始工作并保持记忆力。

foreach (string  fileName in Directory.EnumerateFiles(baseDirectory, "*.*", SearchOption.AllDirectories)) 
{
   // write filename to blocking collection
}

答案 3 :(得分:2)

如果远程服务器上的文件太多,并且您能够在远程服务器上安装新程序,请快速了解CodeCaster的方法是否有效:编写一个程序即可安装在每个服务器上,并侦听某些端口的HTTP请求(或您喜欢的任何Web服务技术)。查询数据库的程序应批量处理每个服务器的文件名,并向每个服务器发送一个请求,其中包含该服务器上的所有文件名。 Web服务检查文件是否存在(由于它现在是本地操作,因此应该很快)并且例如响应一个列表,只包含实际存在的文件名。这应该可以消除大部分协议开销和网络延迟,因为请求数量会大大减少。

答案 4 :(得分:0)

如果我要做这样的任务,我知道瓶颈是:

  1. 磁盘访问延迟(~1ms)
  2. 网络访问延迟(100mbps~0.2ms)
  3. 受磁盘限制的数据库
  4. 最快的是cpu cache,第二个快速是RAM。

    我假设我可以使用额外的数据库表来存储时态数据。 数据库,现在数据我将称为主数据库。

    我将同时完成任务:

    1. 递归目录读取并以块的形式保存到第二个数据库中,用于大约50k文件。
    2. 从主数据库中获取大块记录,并将第一个数据块与第二个数据库中的一个表进行比较 - 未找到的所有文件将写入第三个数据库(并在第一个数据库中标记存在文件)。
    3. 从主数据库中的所有块与第二个数据库相比之后 - 使用第二个数据库检查第三个数据库中的所有块并删除找到的文件。
    4. 在第三个数据库的末尾只会留下不存在的文件,所以我可以从中获取字符串并在主数据库中标记数据。

      可能会有其他改进,可以讨论是否有兴趣。

答案 5 :(得分:0)

如何在从数据库中检索位置时对位置进行排序(db擅长排序)。然后,检查可以从cifs客户端中的缓存目录信息中受益,

你可以获得结果集中下一行的目录列表,然后在dir-list中检查该行是否存在,然后重复检查结果集中的下一行是否在同一目录中,如果是这样,检查已经获取的目录列表,如果没有重复外循环。