使用awk,grep,sed解析大型日志文件(~5gb)的性能问题

时间:2011-08-25 21:34:40

标签: bash unix awk grep logging

我目前正在处理大小约为的日志文件。 5GB。我是解析日志文件和使用UNIX bash的新手,所以我会尽量精确。在搜索日志文件时,我执行以下操作:提供要查找的请求编号,然后可选择将操作作为辅助过滤器提供。典型的命令如下所示:

fgrep '2064351200' example.log | fgrep 'action: example'

这对处理较小的文件很好,但是如果日志文件为5gb,则速度难以忍受。我在线阅读使用sed或awk来提高性能(或者甚至可能是两者的组合)非常棒,但我不确定这是如何实现的。例如,使用awk,我有一个典型的命令:

awk '/2064351200/ {print}' example.log

基本上我的最终目标是能够打印/返回包含字符串的记录(或行号)(可能高达4-5,并且我读过管道很糟糕)以便在日志文件中有效匹配

另一方面,在bash shell中,如果我想使用awk并进行一些处理,那是如何实现的呢?例如:

BEGIN { print "File\tOwner" }
{ print $8, "\t", \
$3}
END { print " - DONE -" }

这是一个非常简单的awk脚本,我认为有一种方法可以将它放入一个单行bash命令中?但我不确定结构如何。

提前感谢您的帮助。欢呼声。

4 个答案:

答案 0 :(得分:18)

您需要执行一些测试以找出瓶颈的位置,以及各种工具的执行速度。尝试这样的测试:

time fgrep '2064351200' example.log >/dev/null
time egrep '2064351200' example.log >/dev/null
time sed -e '/2064351200/!d' example.log >/dev/null
time awk '/2064351200/ {print}' example.log >/dev/null

传统上,egrep应该是最快的(是的,比fgrep更快),但是一些现代实现是自适应的并且自动切换到最合适的搜索算法。如果您有bmgrep(使用Boyer-Moore搜索算法),请尝试使用。通常,sed和awk会更慢,因为它们被设计为更通用的文本操作工具,而不是针对特定的搜索工作进行调整。但它确实取决于实现,找到的正确方法是运行测试。每次运行它们几次,这样你就不会被缓存和竞争过程搞砸了。

正如@Ron指出的那样,您的搜索过程可能是磁盘I / O绑定。如果您将多次搜索同一个日志文件,则首先压缩日志文件可能会更快;这样可以更快地读取磁盘,但需要更多的CPU时间来处理,因为它必须首先解压缩。尝试这样的事情:

compress -c example2.log >example2.log.Z
time zgrep '2064351200' example2.log.Z >/dev/null
gzip -c example2.log >example2.log.gz
time zgrep '2064351200' example2.log.gz >/dev/null
bzip2 -k example.log
time bzgrep '2064351200' example.log.bz2 >/dev/null

我刚用一个相当可压缩的文本文件运行了一个快速测试,发现bzip2压缩得最好,但是后来耗费了更多的CPU时间进行解压缩,所以zgip选项总体来说最快。您的计算机将具有与我的不同的磁盘和CPU性能,因此您的结果可能会有所不同。如果你有任何其他压缩机,也可以尝试它们,和/或尝试不同级别的gzip压缩等。

说到预处理:如果你一遍又一遍地搜索同一个日志,有没有办法预先选择可能感兴趣的日志行?如果是这样,将它们变成一个较小的(可能是压缩的)文件,然后搜索它而不是整个文件。与压缩一样,您可以预先花费一些额外的时间,但每次单独搜索都会更快。

关于管道的说明:在其他条件相同的情况下,通过多个命令管理一个巨大的文件将比单个命令完成所有工作要慢。但是这里所有的东西都不相同,如果在管道中使用多个命令(这是zgrep和bzgrep所做的)会为你带来更好的整体性能,那就去吧。另外,请考虑您是否实际将所有数据传递到整个管道。在您给出的示例fgrep '2064351200' example.log | fgrep 'action: example'中,第一个fgrep将丢弃大部分文件;管道和第二个命令只需要处理包含'2064351200'的小部分日志,因此减速可能可以忽略不计。

tl;博士测试所有的事情!

编辑:如果日志文件是“实时”(即添加新条目),但其中大部分是静态的,您可能可以使用部分预处理方法:压缩(&可能预扫描)日志,然后在扫描时使用压缩的(& /预扫描)版本加上自您执行预扫描后添加的日志部分的尾部。像这样:

# Precompress:
gzip -v -c example.log >example.log.gz
compressedsize=$(gzip -l example.log.gz | awk '{if(NR==2) print $2}')

# Search the compressed file + recent additions:
{ gzip -cdfq example.log.gz; tail -c +$compressedsize example.log; } | egrep '2064351200'

如果您要进行多项相关搜索(例如特定请求,然后是针对该请求的特定操作),您可以保存预扫描版本:

# Prescan for a particular request (repeat for each request you'll be working with):
gzip -cdfq example.log.gz | egrep '2064351200' > prescan-2064351200.log

# Search the prescanned file + recent additions:
{ cat prescan-2064351200.log; tail -c +$compressedsize example.log | egrep '2064351200'; } | egrep 'action: example'

答案 1 :(得分:3)

如果您不知道字符串的顺序,那么:

awk '/str1/ && /str2/ && /str3/ && /str4/' filename

如果你知道他们会在一行中出现一个又一个:

grep 'str1.*str2.*str3.*str4' filename

(注意awk,{print}是默认的动作块,因此如果给出条件则可以省略)

无论你如何分割文件,处理大的文件都会很慢。

答案 2 :(得分:2)

关于命令行上的多行程序,

$ awk 'BEGIN { print "File\tOwner" }
> { print $8, "\t", \
> $3}
> END { print " - DONE -" }' infile > outfile

请注意单引号。

答案 3 :(得分:1)

如果多次处理同一个文件,将其读入数据库可能会更快,甚至可能创建索引。