如何优化含多子目录的网络位置文件搜索效率?
网络目录下大数量.gz文件按时间过滤的优化方案
核心问题分析
当前代码的主要性能瓶颈:
Directory.GetFiles会一次性加载所有匹配*.gz的文件路径到内存,在网络目录下大量文件的场景下,既占用内存又等待时间长- 每次
Where过滤时重复创建FileInfo,每个实例都会发起一次网络请求获取文件属性,重复IO导致耗时剧增
优化方案
1. 改用流式遍历:Directory.EnumerateFiles
EnumerateFiles是延迟加载模式,会逐个返回匹配的文件路径,无需一次性加载所有结果,大幅降低内存占用,同时能提前开始过滤处理,减少等待时间。同时每个文件路径只创建一次FileInfo,避免重复网络请求:
private void PollFolder(string sourceDir) { try { var start = DateTime.Now.AddMonths(-5); var end = DateTime.Now; var filesFromToolDir = Directory.EnumerateFiles(sourceDir, "*.gz", SearchOption.AllDirectories) .Select(path => new FileInfo(path)) // 仅创建一次FileInfo .Where(file => file.CreationTime >= start && file.CreationTime <= end) .ToArray(); } catch (Exception ex) { // 建议添加日志记录异常,避免无感知失败 // Logger.Error(ex, "Polling network folder failed"); } }
2. 并行遍历(需控制并发度)
在网络带宽允许的前提下,可以用并行遍历提升效率,但需限制并发数,避免因过多并发请求导致网络共享服务限流或响应变慢:
private void PollFolder(string sourceDir) { try { var start = DateTime.Now.AddMonths(-5); var end = DateTime.Now; var resultFiles = new List<FileInfo>(); // 控制并发数,建议根据网络环境调整(如2-8) var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 }; Parallel.ForEach(Directory.EnumerateFiles(sourceDir, "*.gz", SearchOption.AllDirectories), parallelOptions, path => { var fileInfo = new FileInfo(path); if (fileInfo.CreationTime >= start && fileInfo.CreationTime <= end) { lock (resultFiles) // 线程安全的添加结果 { resultFiles.Add(fileInfo); } } }); var filesFromToolDir = resultFiles.ToArray(); } catch (Exception ex) { // 异常处理逻辑 } }
3. 利用Windows索引服务(Windows环境专属)
如果是Windows系统,且目标网络共享已开启Windows搜索索引,可以直接查询索引获取结果,无需遍历所有目录,速度会大幅提升。可以通过WMI实现:
using System.Management; private void PollFolderWithWindowsSearch(string sourceDir) { try { var start = DateTime.Now.AddMonths(-5); var end = DateTime.Now; // 格式化WMI要求的日期格式:yyyyMMddHHmmss.ffffff var startStr = start.ToString("yyyyMMddHHmmss.ffffff"); var endStr = end.ToString("yyyyMMddHHmmss.ffffff"); // 转义路径中的反斜杠 var escapedPath = sourceDir.Replace("\\", "\\\\"); // WMI查询语句 var query = $"SELECT Name FROM CIM_DataFile WHERE Extension='gz' " + $"AND CreationDate >= '{startStr}' AND CreationDate <= '{endStr}' " + $"AND Path LIKE '{escapedPath}\\\\%'"; var resultFiles = new List<string>(); using var searcher = new ManagementObjectSearcher(query); foreach (var obj in searcher.Get()) { resultFiles.Add(obj["Name"].ToString()); } // 如需FileInfo可后续转换 var filesFromToolDir = resultFiles.Select(path => new FileInfo(path)).ToArray(); } catch (Exception ex) { // 异常处理逻辑 } }
4. 增量扫描(定期任务场景)
如果是定期执行的扫描任务,可以记录上次扫描的时间戳,仅扫描目录中修改时间晚于上次扫描时间的文件,或者结合目录的LastWriteTime跳过未修改的子目录,减少不必要的遍历。
内容的提问来源于stack exchange,提问作者Datboydozy
相关产品推荐
相关产品推荐

