【问题标题】:Performance optimization searching data in file system文件系统中搜索数据的性能优化
【发布时间】:2019-04-21 08:08:34
【问题描述】:

我有一个网络相关存储,其中大约 500 万个 txt 文件与大约 300 万个事务相关。总数据大小约为 3.5 TB。我必须在该位置搜索以查找与交易相关的文件是否可用,并且必须制作两个单独的报告作为“可用文件”和“不可用文件”的 CSV 文件。我们是 仍然在 JAVA 6 中。我面临的挑战是因为我必须递归地在该位置搜索,由于尺寸巨大,我平均需要大约 2 分钟才能在该位置进行搜索。我正在使用 Java I/O API 进行递归搜索,如下所示。有什么办法可以提高性能吗?

File searchFile(File location, String fileName) {
     if (location.isDirectory()) {
         File[] arr = location.listFiles();
         for (File f : arr) {
             File found = searchFile(f, fileName);
             if (found != null)
                 return found;
         }
     } else {
         if (location.getName().equals(fileName)) {
             return location;
         }
     }
     return null;
}

【问题讨论】:

  • 当你循环遍历这么大的数字时,递归非常糟糕......它增加了 JVM 的开销......但这又取决于你的目录结构有多深...... .

标签: java file search optimization


【解决方案1】:
  • 在目录或网络关联存储中搜索是一种 噩梦。目录太大/深度太大时会花费很多时间。正如您在 Java 6 中一样, 所以你可以遵循旧的时尚方法。列出 CSV 文件中的所有文件,例如 以下。
  • 例如

    找到 . -type f -name '*.txt' >> test.csv 。 (如果是 unix)

    dir /b/s *.txt > test.csv(如果是 Windows)

  • 现在将此 CSV 文件加载到地图中,以将索引作为文件名。加载文件需要一些时间,因为它会很大,但是一旦加载,然后在地图中搜索(因为它将是文件名)会更快,并且会大大减少您的搜索时间。

【讨论】:

  • 感谢您的好主意。我认为在使用上述命令将文件列表作为 CSV 后在本地运行非常有帮助。之后,我从 CSV 生成了一个 HashMap 并完成了我所有的报告处理。现在我能够在更短的时间内生成报告。你拯救了我的一天。干杯!!
【解决方案2】:

您应该采用不同的方法,而不是每次搜索文件时都遍历整个目录,而应该创建一个索引,这是从文件名到文件位置的映射。

基本上:

void buildIndex(Map index, File baseDir) {
    if (location.isDirectory()) {
        File[] arr = location.listFiles();
        for (File f : arr) {
            buildIndex(index, f);
        }
    } else {
        index.put(f.getName(), f);
    }
}

现在您已经有了索引,搜索文件就变得很简单了。

现在你已经在 Map 中获得了文件,你甚至可以使用 Set 操作来找到交集:

Map index = new HashMap();
buildIndex(index, ...);
Set fileSet = index.keySet();
Set transactionSet = ...;
Set intersection = new HashSet(fileSet);
fileSet.retainAll(transactionSet);

或者,如果索引本身太大而无法保存在内存中,您可能希望在 SQLite 数据库中创建索引。

【讨论】:

  • 你是对的,在这种情况下索引本身会变得太大......所以最好根据各种标准划分索引,最简单的是文件名的第一个字符。我有问了一个关于删除重复的问题,但不同人建议的核心逻辑可能适用...stackoverflow.com/questions/12501112/…
  • @Ketan:我建议不要费心尝试用第一个字符或类似的东西分割索引之类的技巧。只需让 SQLite 处理数据管理即可。但是,500 万个文件名是极少量的数据。它最多只有一个千兆字节(可能要少得多),这在现代机器中仍然相当小。除非您的数据比这大几个数量级,否则您可能永远不需要使用 Map 以外的任何东西。
  • SQLite 不是我的选择。我可以通过创建索引来减少时间,但由于大量数据存储在共享驱动器中,因此加载所有文件以创建仍然需要时间索引地图。非常感谢索引提示!
【解决方案3】:

我不知道答案,但从算法的角度来看,你的程序的复杂度是最差的。每次查找单个事务,它会迭代所有文件(500 万)。你有 300 万笔交易。

我的建议是迭代文件(500 万个文件)并根据文件名建立索引。然后迭代事务并搜索索引而不是全扫描。 或者可能有第三方免费工具可以索引大型文件系统,然后外部应用程序(在本例中为您的 java 应用程序)可以访问该索引。如果您找不到那种工具,最好自己发明它(然后您可以以适合您要求的最佳方式构建索引)。

【讨论】:

    【解决方案4】:

    您可以使用 NIO FileVisitor,在 java 6 中可用。

    Path findTransactionFile(Path root) {
        Path transactionFile = null;
        Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                if (/* todo dir predicate*/ false) {
                    return FileVisitResult.SKIP_SUBTREE; // optimization
                }
                return FileVisitResult.CONTINUE;
            }
    
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                if (/* todo file predicate*/ true) {
                    transactionFile = file;
                    return FileVisitResult.TERMINATE; // found    
                }
                return FileVisitResult.CONTINUE;
            }
        });
    
        return transactionFile;
    }
    

    【讨论】:

    • 我想我错了,它从 java 7 开始可用,你可以使用 Apache VFS 在旧 java 中实现类似的行为
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-04
    • 1970-01-01
    • 2014-03-01
    相关资源
    最近更新 更多