正在验证网络上是否存在3.5亿个文件
本文关键字:5亿 文件 存在 是否 验证 网络 | 更新日期: 2023-09-27 18:25:55
我有一个SQL Server表,其中有大约300000000个绝对UNC路径,我正在尝试(快速)验证每个路径,以确保SQL Server表中的路径实际上以文件的形式存在于磁盘上。
从表面上看,我以50000为一批地查询表,并在进行过程中递增计数器以推进我的批次。
然后,我使用一个数据读取器对象来存储我的当前批处理集,并在批处理中循环,使用File.Exists(path)
命令检查每个文件,就像下面的例子一样。
问题是,我在一个四核3.4ghz i5和16gb ram上以每秒最多1000个文件的速度处理,这将需要几天的时间。有更快的方法吗?
我在SQL Server表上确实有一个列存储索引,我已经对它进行了分析;1s,因此在向.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();
编辑:添加一些附加信息:
考虑通过数据库本身来完成这一切;它是一个拥有2tb内存和64核的sqlserver企业级服务器。问题是sql server服务帐户无法访问承载数据的nas路径,因此我的cmdshell通过SP运行失败(我无法控制AD内容),并且UNC路径有数十万个基于文件MD5哈希的单独子目录。所以枚举目录的内容最终是没有用的,因为你可能有一个10个目录深的文件,只包含1个文件。这就是为什么我必须进行文字完整路径匹配/检查。
哦,总的来说,这条路很长。实际上,在我意识到这相当于90gb的数据之前,我曾尝试将它们全部加载到内存中的列表中(哈哈,哎呀)。完全同意其他关于线程化的评论。数据库速度极快,一点也不担心。虽然我没有考虑过SMB聊天,但这很可能是我遇到的问题JRats 13小时前
哦!我也只是在文件不存在的情况下更新数据库。如果是,我不在乎。因此,我的数据库运行被最小化为获取一批路径。基本上,我们将一堆数据从较慢的存储迁移到了这个灵活的设备上,我被要求通过编写一些东西来验证每个文件的存在性,以确保一切都能正常进行。
穿线帮了不少忙。我跨越了4个线程进行文件检查,处理能力达到了每秒3300条记录,这要好得多,但如果可以的话,我仍然希望能更快。有没有一个好的方法来判断我是否被SMB流量绑定?我注意到,当我试图将线程数提高到4或5时,我的速度降到了涓涓细流;我以为我可能在某个地方陷入僵局,但没有。
哦,我无法进行FilesOnNetwork检查,这正是你所说的原因,与我想要检查的文件相比,那里实际托管的文件数量是我的3到4倍。这个灵活的设备上可能有大约1.5亿个文件。
优化SQL端在这里是没有意义的,因为您是文件IO绑定的。
我会使用Directory.EnumerateFiles
来获得所有存在文件的列表。枚举目录中的文件应该比单独测试每个文件快得多。
您甚至可以完全颠倒问题,并将该文件列表大容量插入到数据库临时表中,这样您就可以在数据库中执行基于SQL的集合处理。
如果你想继续单独测试,你可能应该同时进行。目前尚不清楚该进程是否真的是磁盘绑定的。可能是网络或CPU绑定。
并行性将通过重叠多个请求来提供帮助。问题可能出在网络延迟上,而不是带宽上。在DOP 1下,在任何给定时间至少有一台机器处于空闲状态。有时两者都处于闲置状态。
与我想要检查的相比,那里实际托管的文件数量是原来的3到4倍
使用dir /b
命令将所有文件名的列表通过管道传输到.txt文件中。在有文件的机器上本地执行,但如果不可能远程执行。然后使用bcp
将它们大容量插入数据库的表中。然后,您可以在一个SQL查询中进行快速的存在性检查,该查询将得到高度优化。你将得到一个散列加入。
如果你想并行这个策略的dir
阶段,你可以为此编写一个程序。但也许没有必要这样做,尽管dir是单线程的,但它已经足够快了。
瓶颈很可能是网络流量,或者更具体地说:SMB流量。您的计算机与SMB进行通信以从网络存储中检索文件信息。SMB流量是"聊天",你需要一些消息来检查文件的存在和你的读取权限。
值得一提的是,在我的网络上,我可以通过SMB每秒查询大约100个文件的存在,而递归列出15K文件需要10秒。
可以更快的是提前检索上的远程目录列表。如果目录结构是可预测的,这将是微不足道的——和如果存储在这些目录中不包含许多不相关的文件。
然后你的代码会像这样:
HashSet<string> filesOnNetwork = new HashSet<string>(Directory.EnumerateFiles(
baseDirectory, "*.*", SearchOption.AllDirectories));
foreach (var fileToCheck in filesFromDatabase)
{
fileToCheckExists = filesOnNetwork.Contains(fileToCheck);
}
如果网络上的文件比您需要检查的文件多得多,这可能会产生不利影响,因为filesOnNetwork
的填充和搜索将成为应用程序的瓶颈。
在您当前的解决方案中,获得50000个批次并打开和关闭连接没有任何作用,只会减慢速度。DataReader流。只要打开一次,一次读一个。在封面下,阅读器将一次发送一批。当你只读了10行时,DataReader不会试图用30000000行阻塞客户端。
我认为您担心优化SQL 的最快步骤读取
验证文件路径将是最慢的步骤
我喜欢CodeCaster的答案,但在3.5亿的情况下,你将使用.NET进入对象大小限制。通过读取哈希集,直到完成这一步,它才会开始工作。
我会使用一个BlockingCollection和两个集合
- 枚举文件
- 写入数据库
最慢的步骤是读取文件名,所以要尽可能快地读取,不要中断。在靠近存储设备的设备上执行此操作。在连接SAN的设备上运行该程序。
我知道你会说写数据库很慢,但它只需要比枚举文件快。只需为find创建一个二进制列-不要将完整的文件名写入#temp。我敢打赌,一个(优化的)更新比枚举文件更快。一次更新10000行,减少往返行程。我会异步更新,这样你就可以在处理当前更新的同时构建下一个更新。
最后,您必须检查数据库中是否有任何未标记为已找到的文件。
不要先去中间集合。直接处理枚举。这可以让你立即开始工作,并保持记忆力。
foreach (string fileName in Directory.EnumerateFiles(baseDirectory, "*.*", SearchOption.AllDirectories))
{
// write filename to blocking collection
}
如果CodeCaster的方法由于远程服务器上的文件太多而不起作用,并且如果您能够在远程服务器上安装新程序,那么一个快速的想法是:编写一个程序,安装在每台服务器上,并侦听HTTP请求的某个端口(或您喜欢的任何web服务技术)。查询数据库的程序应该对每台服务器的文件名进行批处理,并向每台服务器发送一个包含该服务器上所有文件名的请求。web服务检查文件是否存在(这应该很快,因为它现在是一个本地操作),并以例如仅包含实际存在的文件名的列表作为响应。这将消除大部分协议开销和网络延迟,因为请求数量大大减少。
如果我要做这样的任务,我知道瓶颈是:
- 磁盘访问延迟(~1ms)
- 网络访问延迟(100mbps~0.2ms)
- 数据库受磁盘限制
最快的是cpu缓存,第二快的是RAM。
我假设,我可以使用额外的数据库表来存储时间数据。数据库中现在的数据我将调用主数据库。
我将并行执行任务:
- 递归目录读取并保存到第二个数据库中的大约50k个文件
- 从主数据库中获取记录块,并将第二个数据库中的一个块和一个表进行比较——所有未找到的文件都将写入第三个数据库(并标记第一个数据库中存在的文件)
- 在将主数据库中的所有块与第二个数据库进行比较后,将第三个数据库中的全部块和第二个数据进行检查,并删除找到的文件
最后在第三个数据库中只留下不存在的文件,所以我可以从中获取字符串并在主数据库中标记数据。
可能会有额外的改进,如果感兴趣可以讨论。
如何在从数据库检索位置时对其进行排序(数据库擅长排序)。然后,检查可以从cifs客户端中缓存的目录信息中受益
您可以获取结果集中下一行的目录列表,然后检查该行在目录列表中是否存在,然后重复检查结果集中的下一行是否在同一目录中,如果是,则检查已获取的目录列表(如果不是重复外循环)。