我有一个包含大约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瓶颈。
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左右的文件。
发布于 2015-12-05 20:43:38
在这里优化SQL端是没有意义的,因为您受文件IO的限制。
我将使用Directory.EnumerateFiles来获取所有存在的文件的列表。枚举目录中的文件应该比单独测试每个文件快得多。
您甚至可以完全颠倒问题,并将该文件列表批量插入到数据库临时表中,这样您就可以在数据库中进行基于SQL的集合处理。
如果您想单独进行测试,您可能应该并行执行此操作。目前还不清楚这个过程是否真的是磁盘限制的。可能是网络或CPU限制的。
并行性通过重叠多个请求在这里会有所帮助。可能出现问题的是网络延迟,而不是带宽。在DOP 1处,在任何给定时间至少有一台机器是空闲的。有时两者都是空闲的。
与我想要检查的文件相比,实际托管的文件数量是3到4倍
使用dir /b命令将所有文件名列表通过管道传输到.txt文件。在包含这些文件的计算机上本地执行该命令,但如果不可能,则远程执行。然后使用bcp将它们大容量插入到数据库的表中。然后,您可以在单个SQL查询中进行快速存在检查,这将是高度优化的。您将得到一个散列连接。
如果你想并行化这个策略的dir阶段,你可以为此编写一个程序。但也许没有必要这样做,尽管dir是单线程的,但它已经足够快了。
发布于 2015-12-05 20:43:24
瓶颈很可能是网络流量,或者更具体地说: 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将成为应用程序的瓶颈。
发布于 2015-12-06 00:36:50
在您当前的解决方案中,获取50,000个批次并打开和关闭连接没有任何意义,只会使事情变得缓慢。DataReader streams。只需打开它一次,一次只读一个。在封面下,阅读器将一次发送批次。当您只读取了10行数据时,DataReader不会尝试将300,000,000行数据塞入客户端。
我想您担心的是从SQL中优化最快的步骤读取
验证文件路径将是最慢的步骤
我喜欢CodeCaster的回答,但是在3.5亿的情况下,.NET会达到对象大小的限制,而且通过读入HashSet,它在这一步完成之前不会开始工作。
我将使用具有两个集合的BlockingCollection
最慢的步骤是读取文件名,所以要尽可能快地读取,并且不要中断。在靠近存储设备的设备上执行此操作。在连接SAN的设备上运行该程序。
我知道你会说写入db很慢,但它只需要比枚举文件快就行了。只需使用二进制列表示found --不要将完整的文件名写入#temp。我敢打赌,一个(优化的)更新比枚举文件更快。一次将更新分成10,000行,以减少往返次数。我将进行异步更新,这样您就可以在当前更新正在处理的同时构建下一个更新。
最后,您需要检查DB中是否有任何未标记为找到的文件。
不要先去中间集合。直接处理枚举。这使您可以立即开始工作,并保持内存较低。
foreach (string fileName in Directory.EnumerateFiles(baseDirectory, "*.*", SearchOption.AllDirectories))
{
// write filename to blocking collection
}https://stackoverflow.com/questions/34104575
复制相似问题