【问题标题】:Fastest approach to search within file contents of a directory在目录的文件内容中搜索的最快方法
【发布时间】:2017-04-07 21:34:39
【问题描述】:

我有一个目录,其中包含我拥有的程序用户的文件。该目录中有大约 70k 个 json 文件。

当前的搜索方法是使用globforeach。它变得非常缓慢并且占用了服务器。有什么好方法可以更有效地搜索这些文件吗?我在 Ubuntu 16.04 机器上运行它,如果需要,我可以使用 exec

更新:

这些是 json 文件,每个文件都需要打开以检查它是否包含搜索查询。循环遍历文件非常快,但是当它需要打开每个文件时,需要相当长的时间。

这些不能使用 SQL 或 memcached 进行索引,因为我正在使用 memcached 处理其他一些事情。

【问题讨论】:

  • 能否像在 SQL 数据库中那样离线索引以减少负载?
  • 您应该使用某种形式的缓存。你可以有一个脚本,每 15 分钟索引一次图像(或者它们经常改变)并将它们保存到数据库中,然后从数据库中获取图像,这应该会更快。
  • 实际上我发现使用glob 的方法比递归迭代器更快,而且我可以更好/更轻松地过滤它。您可以尝试对glob 和递归迭代器进行基准测试,看看自己更快。这两个是我知道的唯一选项,如果您不想拥有关于文件的 DBMS 表并监视文件夹的更改。
  • Apache SOLR 是一个选项?
  • 如果你可以使用exec,那么看看findgrep的命令

标签: php file search


【解决方案1】:

正如您自己暗示的那样,为了使搜索尽可能高效,您需要将任务交给专为此目的设计的工具。

我说,go beyond grep,看看better than ack 是什么。另外,请查看ag,然后选择ripgrep,因为它是镇上同类产品中最好的。


实验

我在一台低规格的笔记本电脑上用ack 做了一个小实验。我在 19,501 文件中搜索了现有的类名。结果如下:

$ cd ~/Dev/php/packages
$ ack -f | wc -l 
19501

$ time ack PHPUnitSeleniumTestCase | wc -l
10
ack PHPUnitSeleniumTestCase  7.68s user 2.99s system 21% cpu 48.832 total
wc -l  0.00s user 0.00s system 0% cpu 48.822 total

我做了同样的实验,这次是ag。它真的让我感到惊讶:

$ time ag PHPUnitSeleniumTestCase | wc -l
10
ag PHPUnitSeleniumTestCase  0.24s user 0.98s system 13% cpu 9.379 total
wc -l  0.00s user 0.00s system 0% cpu 9.378 total

我对结果感到非常兴奋,我继续尝试ripgrep。更好:

$ time rg PHPUnitSeleniumTestCase | wc -l
10
rg PHPUnitSeleniumTestCase  0.44s user 0.27s system 19% cpu 3.559 total
wc -l  0.00s user 0.00s system 0% cpu 3.558 total

试用这一系列工具,看看哪种工具最适合您的需求。


P.S. ripgreporiginal author 在此帖下留下了 a comment,说是 ripgrep is faster than {grep, ag, git grep, ucg, pt, sift}。有趣的阅​​读,精彩的工作。

【讨论】:

  • 您是否在每次调用之前刷新了磁盘缓存?如果不是,那么您很有可能将苹果与橙子进行比较。在第一次测试期间,操作系统必须从驱动器中读取文件。它还将它们放在 RAM 中以进行缓存(前提是您有一些空闲 RAM)。在第二次和第三次测试中,这些文件可能不是从硬盘读取,而是从 RAM 读取,速度要快几个数量级。
  • 好点,但我很确定情况并非如此。刷新磁盘缓存后,运行ack 两次将产生几乎相同的基准。 agrg 更好的数字背后的原因在于它们不同的实现。为了 100% 确定这一点,我刚刚对刷新磁盘缓存做了同样的实验,结果相同。
  • @matt 这里的结果和我自己的一致:blog.burntsushi.net/ripgrep
【解决方案2】:

根据您是使用 SSD 还是 HDD 来存储文件,答案会有所不同。

硬盘

对于 HDD最可能的瓶颈不是 PHP,而是 HDD 可以处理的 I/O 操作数量很少。如果可行,我强烈建议迁移到 SSD 或使用 RAM 磁盘。

假设您无法将目录移动到 SSD。这意味着您被困在可以执行 ~70-~200 IOPS(每秒 I/O 操作,假设您的系统不缓存 RAM 目录中的文件)的 HDD 上。您最好的选择是尽量减少 I/O 调用,如 fstat、filemtime、file_exists 等,并专注于读取文件的操作(file_get_contents() 等)。

HDD 和操作系统允许 HDD 控制器对 I/O 操作进行分组,以解决可用的低 IOPS 问题。例如,如果两个文件在 HDD 上彼此靠近,您可以读取其中两个或多个文件,但只需读取其中一个(我在这里简化了一些事情,但我们不要过多讨论技术细节)。因此,与某些想法相反,一次读取多个文件(例如使用线程程序、xargs 等)可能会大大提高性能。

不幸的是,只有当这些文件在物理 HDD 上彼此靠近时才会出现这种情况。如果您真的想加快速度,您应该首先考虑使用应用程序读取文件的顺序,因为这对于下一步至关重要。一旦你弄清楚了,你可以完全擦除硬盘驱动器(假设你可以这样做)并按照你确定的顺序顺序写入文件。这应该将文件并排放置并提高并行文件读取时的有效 IOPS。

接下来,您需要转到 shell 并使用可以并行处理文件的程序 - PHP 支持 pthread,但不要走这条路。如果您计划使用单线程应用程序,带有多个进程的xargs-P 选项)可能会有所帮助。读取 shell_exec() 输出并在您的 PHP 程序中处理它。

固态硬盘

由于 HDD 并行处理可能会有所帮助,但最好先查看您的代码,因为 I/O 可能不是问题。

【讨论】:

    猜你喜欢
    • 2017-12-31
    • 1970-01-01
    • 2017-10-26
    • 1970-01-01
    • 2013-09-04
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    相关资源
    最近更新 更多