如何加速基于 grep 搜索的脚本?

如何加速基于 grep 搜索的脚本?

有一个非常大的文本文件,其中有两个逗号分隔的值:

78414387,10033
78769989,12668
78771319,13677
78771340,13759
80367563,16336
81634533,10025
82878571,10196
110059366,10218
110059411,10812
110059451,10067

我需要在日志文件中搜索这些值,如下所示:

- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c737e3d80d7def296c7| id=278832702| version=28| timestamp=1467432051000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c732f18c26fe604fd04| id=284057302| version=9| timestamp=1467432051000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c747e3d80d7def296c8| id=357229| version=1151| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c742f18c26fe604fd05| id=279832706| version=35| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c744697ddb976cf5a95| id=354171| version=503| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:53 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c754697ddb976cf5a96| id=355638| version=1287| timestamp=1467432053000

我的脚本:

#!/bin/bash
COUNT=0
while IFS=',' read ID VERSION; do
    VERSION=`echo $VERSION |col -bx`
    if (grep "id=${ID}| version=$VERSION" worker-6715.log.2016-$1.log.* > /dev/null); then
            let "COUNT++"
    else
            echo "$ID, $VERSION FAIL"
            exit 2

    fi
done < recon.txt

echo "All OK, $COUNT checked"
  • 如果我从日志文件中删除不必要的字段,这会加快执行速度吗?
  • 如果我创建一个 RAM 设备并将日志文件复制到那里,这会加快执行速度还是我的红帽Linux 6(Hedwig) 无论如何缓存文件?还有其他建议吗?

答案1

您的瓶颈是recon.txt在 shell 中逐行读取文件。要获得失败的行,您可以预处理日志中的行,使其看起来像 中的行recon.txt,然后用于comm(1)查找设置的差异,可能如下所示:

comm -23 \
    <(sort -u recon.txt) \
    <(sed 's/.*| id=\([0-9]*\)| version=\([0-9]*\)|.*/\1,\2/' worker-6715.log.2016-$1.log.* | \
        sort -u)

这假设有一个可以处理构造的 shell <(...)。另请注意,结果中的行不保留 中行的顺序recon.txt。保持这个顺序会更困难(而且更慢)。

如果您还需要成功计数,则可以反其道而行之,进行预处理,recon.txt使其看起来像在日志中找到的内容,然后使用fgrep(1)grep -F进行搜索。将区域设置设置为C还可以在某些系统上显着加快速度。因此:

COUNT=$( \
    sed 's/\([0-9]*\),\([0-9]*\)/| id=\1| version=\2|/' recon.txt | \
    LC_ALL=C fgrep -f - worker-6715.log.2016-$1.log.* | \
    wc -l )

这假设recon.txt不包含重复项,并且每行在recon.txt所有日志中最多匹配一次。取消第一个限制将很困难。第二个可以通过仔细选择来解除comm(1)

答案2

正如所指出的,这里的主要问题是 shell 循环。我会awk首先处理日志文件,连接idversion值并将结果保存到数组中,然后读取recon.txt并在每一行上检查它是否在数组中,如果不是 - 将行内容保存在变量中texit立即(执行END块)。在块中,如果thenEND中保存了一行带有消息,否则只打印 OK 消息,但没有。的行数:texit 2recon.txt

awk 'NR==FNR{j=$9","$10;gsub(/[^0-9,]/, "", j);a[j]++;next}
!($0 in a){t=$0;{exit}}
END{if (t){print t, "FAIL"; exit 2}
else{print "All OK,", FNR, "checked"}}' logfile recon.txt

id这假设和的数值version分别位于第 9 和第 10 字段。如果不是这种情况,您可以使用正则表达式,例如佐藤确实 - 也就是说,如果你的awk风格支持反向引用,否则类似这样的事情会发生:

NR==FNR{sub(/.* id=/, "");sub(/\| version=/, ",");sub(/\|.*/, ""));a[$0]++;next}

答案3

我将这些文件放入数据库(任何你喜欢的类型)。也许甚至不导入大文件,而是从生成该输出的应用程序中逐行放入。

这样DB引擎就不会浪费资源每次调用grep而是使用C编译的内部函数。

不要忘记在导入后(如果导入整个文件)或创建表后(如果逐行插入记录)创建索引。

答案4

首先将 recon.txt 转换为 grep -F 可以快速使用的格式:

perl -pe 's/,/| version=/' recon.txt > recon.grep-F

那么你的情况就如上面所描述的https://www.gnu.org/software/parallel/parallel_examples.html#example-grepping-n-lines-for-m-regular-expressions

这里的文字适合您的问题:

grep 大量正则表达式的大文件的最简单解决方案是:

grep -f regexps.txt bigfile

或者如果正则表达式是固定字符串:

grep -F -f recon.grep-F worker.log

有 2 个限制因素:CPU 和磁盘 I/O。 CPU 很容易测量:如果 grep 占用 >90% CPU(例如,运行 top 时),那么 CPU 是一个限制因素,并行化将加快这一速度。如果不是,则磁盘 I/O 是限制因素,并且根据磁盘系统,并行化可能会更快或更慢。唯一确定的方法就是测量。

如果 CPU 是限制因素,则应在正则表达式上进行并行化:

cat recon.grep-F | parallel --pipe -L1000 --round-robin grep -F -f - worker.log

如果一行与多个正则表达式匹配,则该行可能会重复。该命令将为每个 CPU 启动一个 grep 并为每个 CPU 读取一次大文件,但由于这是并行完成的,除了第一个读取之外的所有读取都将缓存在 RAM 中。根据 regexp.txt 的大小,使用 --block 10m 而不是 -L1000 可能会更快。如果 regexp.txt 太大而无法放入 RAM,请删除 --round-robin 并调整 -L1000。这将导致bigfile被读取更多次。

一些存储系统在并行读取多个块时性能更好。对于某些 RAID 系统和某些网络文件系统来说确实如此。并行读取大文件:

parallel --pipepart --block 100M -a worker.log -k grep -F -f recon.grep-F

这会将 bigfile 分割成 100MB 的块,并对每个块运行 grep。要并行读取 bigfile 和 regexp.txt,请使用 --fifo 将两者结合起来:

parallel --pipepart --block 100M -a worker.log --fifo cat recon.grep-F \
\| parallel --pipe -L1000 --round-robin grep -f - {}

如果一行与多个正则表达式匹配,则该行可能会重复。

相关内容