首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >验证网络上是否存在3.5亿个文件

验证网络上是否存在3.5亿个文件
EN

Stack Overflow用户
提问于 2015-12-05 19:51:47
回答 6查看 4.2K关注 0票数 27

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

从表面上看,我正在以50,000个为一批的批量查询表,并递增一个计数器,以使我的批次不断前进。

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

问题是,我正在处理大约。在具有16 is内存的3.4 per四核i5上,每秒最多1000个文件,这将需要几天的时间。有没有更快的方法来做这件事?

我确实在SQL Server表上有一个列存储索引,并且我已经对其进行了分析。我在<1秒内获得了50k条记录的批处理,因此在向.net控制台应用程序发出批处理时,这不是一个SQL瓶颈。

代码语言:javascript
复制
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();

编辑:添加一些附加信息:

我认为这一切都是通过数据库本身来实现的;它是拥有2tb内存和64个核心的sql server企业版。问题是sql server服务帐户无法访问托管数据的nas路径,因此我的cmdshell通过出现故障的SP运行(我无法控制AD内容),并且UNC路径基于文件的MD5散列具有数十万个单独的子目录。因此,枚举目录的内容最终是没有用的,因为您可能只有10个目录中包含1个文件的文件。这就是为什么我必须进行文字全路径匹配/检查的原因。

哦,而且路径一般都很长。在意识到这相当于90G的数据(lol,oops)之前,我实际上尝试将它们都加载到内存中的列表中。完全同意其他关于线程的评论。数据库非常快,一点也不担心。虽然没有考虑过SMB chatter,但这很可能是我遇到的问题。- 13小时前的JRats

噢!如果文件不存在,我也只更新数据库。即使是这样,我也不在乎。因此,我的数据库运行被最小化到抓取批量路径。基本上,我们将一大堆数据从较慢的存储设备迁移到这个灵活的设备上,我被要求通过编写一些东西来验证每个文件的存在,以确保所有内容都能真正完成。

线程化很有帮助。我跨越了4个线程的文件检查,使我的处理能力达到了大约3300条记录/秒,这要好得多,但我仍然希望如果我能得到更快的速度。有没有好的方法来判断我是否受到SMB流量的限制?我注意到,一旦我试图将我的线程数增加到4或5,我的速度就会下降到细流;我认为我可能在某个地方死锁了,但事实并非如此。

哦,因为你说的确切原因,我不能做FilesOnNetwork检查,与我想要检查的文件相比,实际上有3到4倍的文件托管在那里。在这个灵活的设备上可能有15b左右的文件。

EN

回答 6

Stack Overflow用户

发布于 2015-12-05 20:43:38

在这里优化SQL端是没有意义的,因为您受文件IO的限制。

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

您甚至可以完全颠倒问题,并将该文件列表批量插入到数据库临时表中,这样您就可以在数据库中进行基于SQL的集合处理。

如果您想单独进行测试,您可能应该并行执行此操作。目前还不清楚这个过程是否真的是磁盘限制的。可能是网络或CPU限制的。

并行性通过重叠多个请求在这里会有所帮助。可能出现问题的是网络延迟,而不是带宽。在DOP 1处,在任何给定时间至少有一台机器是空闲的。有时两者都是空闲的。

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

使用dir /b命令将所有文件名列表通过管道传输到.txt文件。在包含这些文件的计算机上本地执行该命令,但如果不可能,则远程执行。然后使用bcp将它们大容量插入到数据库的表中。然后,您可以在单个SQL查询中进行快速存在检查,这将是高度优化的。您将得到一个散列连接。

如果你想并行化这个策略的dir阶段,你可以为此编写一个程序。但也许没有必要这样做,尽管dir是单线程的,但它已经足够快了。

票数 20
EN

Stack Overflow用户

发布于 2015-12-05 20:43:24

瓶颈很可能是网络流量,或者更具体地说: SMB流量。您的机器与SMB通信,以从网络存储中检索文件信息。SMB流量是“聊天”的,你需要几条消息来检查文件的存在和你是否有权限读取它。

值得一提的是,在我的网络上,我可以通过SMB每秒查询大约100个文件的存在,而递归列出15K文件需要10秒。

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

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

代码语言:javascript
复制
HashSet<string> filesOnNetwork = new HashSet<string>(Directory.EnumerateFiles(
                            baseDirectory, "*.*", SearchOption.AllDirectories));

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

如果网络上的文件比您需要检查的文件多得多,这可能会起到不利的作用,因为填充和搜索filesOnNetwork将成为应用程序的瓶颈。

票数 11
EN

Stack Overflow用户

发布于 2015-12-06 00:36:50

在您当前的解决方案中,获取50,000个批次并打开和关闭连接没有任何意义,只会使事情变得缓慢。DataReader streams。只需打开它一次,一次只读一个。在封面下,阅读器将一次发送批次。当您只读取了10行数据时,DataReader不会尝试将300,000,000行数据塞入客户端。

我想您担心的是从SQL中优化最快的步骤读取

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

我喜欢CodeCaster的回答,但是在3.5亿的情况下,.NET会达到对象大小的限制,而且通过读入HashSet,它在这一步完成之前不会开始工作。

我将使用具有两个集合的BlockingCollection

  • 枚举文件
  • write to db

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

我知道你会说写入db很慢,但它只需要比枚举文件快就行了。只需使用二进制列表示found --不要将完整的文件名写入#temp。我敢打赌,一个(优化的)更新比枚举文件更快。一次将更新分成10,000行,以减少往返次数。我将进行异步更新,这样您就可以在当前更新正在处理的同时构建下一个更新。

最后,您需要检查DB中是否有任何未标记为找到的文件。

不要先去中间集合。直接处理枚举。这使您可以立即开始工作,并保持内存较低。

代码语言:javascript
复制
foreach (string  fileName in Directory.EnumerateFiles(baseDirectory, "*.*", SearchOption.AllDirectories)) 
{
   // write filename to blocking collection
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34104575

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档