Parsing 解析非常大的日志文件(>;1Gb,<;5Gb)

Parsing 解析非常大的日志文件(>;1Gb,<;5Gb),parsing,text,logging,Parsing,Text,Logging,我需要解析非常大的日志文件(>1Gb,处理这类事情的能力惊人地好。用Perl编写一个原型,并将其性能与从存储介质读取数据的速度进行比较。我猜您将受到I/O限制,这意味着使用C不会带来性能提升。您可能想看看Hadoop(java)或Hadoop流媒体(使用任何可执行文件或脚本运行Map/Reduce作业)。但实际上,使用AWK。它的性能并不差,甚至与Perl等相比。cource Map/Reduce的性能也不错,但是将文件拆分为适当的块的开销如何 尝试AWK如果您编写了自己的解决方案,那么从文件中

我需要解析非常大的日志文件(>1Gb,处理这类事情的能力惊人地好。

用Perl编写一个原型,并将其性能与从存储介质读取数据的速度进行比较。我猜您将受到I/O限制,这意味着使用C不会带来性能提升。

您可能想看看Hadoop(java)或Hadoop流媒体(使用任何可执行文件或脚本运行Map/Reduce作业)。

但实际上,使用AWK。它的性能并不差,甚至与Perl等相比。cource Map/Reduce的性能也不错,但是将文件拆分为适当的块的开销如何


尝试AWK

如果您编写了自己的解决方案,那么从文件中读取更大的数据块并进行批处理(而不是使用,比如,
readline()
)可能会使您受益匪浅使用这种方法,您需要注意,您可能没有检索到最后一行的全部内容,因此需要一些逻辑来处理


我不知道您会意识到什么性能优势,因为我还没有测试过,但我成功地利用了类似的技术。

关键不在于语言,因为问题是I/O限制的,所以选择您觉得最适合的语言

关键是它是如何编码的。只要你不把整个文件加载到内存中——一次加载块,一次保存数据块,你就不会有问题,这样效率会更高

Java有一个PushbackInputStream,这可能使编写代码更容易。其思想是,您可以猜测要读取多少,如果读取的数据太少,则将数据推回,并读取更大的数据块


然后,当您读取的数据太多时,处理数据,然后将剩余的位向后推,继续循环的下一次迭代。

类似的方法应该可以工作

use strict;
use warnings;

my $filename = shift @ARGV;

open my $io, '<', $filename or die "Can't open $filename";

my ($match_buf, $read_buf, $count);

while (($count = sysread($io, $read_buf, 1024, 0)) != 0) {
    $match_buf .= $read_buf;
    while ($match_buf =~ s{TIMESTAMP=(\d{14});PARAM1=([^;]+);PARAM2=([^;]+);PARAM3=([^;]+);}{}) {
        my ($timestamp, @params) = ($1, $2, $3, $4);
        print $timestamp ."\n";
        last unless $timestamp;
    }
}
使用严格;
使用警告;
我的$filename=shift@ARGV;

打开我的$io,“这在Perl、Awk或C中很容易处理。以下是C版本的开始:

#include <stdio.h>
#include <err.h>

int
main(int argc, char **argv)
{
        const char      *filename = "noeol.txt";
        FILE            *f;
        char            buffer[1024], *s, *p;
        char            line[1024];
        size_t          n;
        if ((f = fopen(filename, "r")) == NULL)
                err(1, "cannot open %s", filename);
        while (!feof(f)) {
                n = fread(buffer, 1, sizeof buffer, f);
                if (n == 0)
                       if (ferror(f))
                               err(1, "error reading %s", filename);
                       else
                               continue;
                for (s = p = buffer; p - buffer < n; p++) {
                        if (*p == ';') {
                                *p = '\0';
                                strncpy(line, s, p-s+1);
                                s = p + 1;
                                if (strncmp("TIMESTAMP", line, 9) != 0)
                                        printf("\t");
                                printf("%s\n", line);
                        }
                }
        }
        fclose(f);
}
#包括
#包括
int
主(内部argc,字符**argv)
{
const char*filename=“noeol.txt”;
文件*f;
字符缓冲区[1024],*s,*p;
字符行[1024];
尺寸;
if((f=fopen(文件名,“r”))==NULL)
错误(1,“无法打开%s”,文件名);
而(!feof(f)){
n=fread(缓冲区,1,缓冲区大小,f);
如果(n==0)
if(费罗(f))
错误(1,“读取%s时出错”,文件名);
其他的
继续;
对于(s=p=buffer;p-buffer< /代码> 我知道这是一种异国语言,可能不是最好的解决办法,但是当我有特设数据时,我认为

< P>这个关于Python生成器的介绍让我大吃一惊:

David M.Beazley通过基本上为每个处理步骤定义一个生成器,展示了如何处理数十亿字节的日志文件。然后,这些生成器相互“插入”,直到您拥有一些简单的实用程序功能

lines = lines_from_dir("access-log*","www")
log   = apache_log(lines)
for r in log:
    print r
然后可用于各种查询:

stat404 = set(r['request'] for r in log
                if r['status'] == 404)

large = (r for r in log
           if r['bytes'] > 1000000)
for r in large:
    print r['request'], r['bytes']
他还展示了性能与标准unix工具(如grep、find等)的性能相比是非常好的。 当然这是Python,它比perl或awk脚本更容易理解,更重要的是更容易定制或适应不同的问题集

(上面的代码示例是从演示幻灯片中复制的。)

听起来像是针对以下人员的工作:

sed-e的/;\?[A-Z0-9]*=/|/g'-e的/\(^\|\)\(;$\)//g'output

不需要深奥的解决方案。5gb没有那么大。SATA2是300 mb/秒,因此需要大约20秒。当然,如果你只想这样做一次。@ebo:300 Mbps的数字是理论上的最大值。如果没有任何争用,典型的硬盘驱动器实际上可以获得75-150 Mbps。如果你正确设置了RAID阵列,那么你实际上可以启动against接口带宽限制。这需要使用awk的RS变量(=Perl的$/),因为文件不包含换行符。鉴于“man-awk”在这里说“RS是正则表达式[当不是单个字符时]”,这对性能有什么影响?我在这个问题上添加了一个工作脚本。我喜欢有人将工具很好地用于它不是设计用来做的事情。我不是perl程序员,但它看起来像是以1024b块读取文件。这会不会错过块末尾的时间戳,例如在pos 1020处?第一块只包含“时间”,第二块以“STAMP…”这样正则表达式就不匹配了。谢谢,你说得对。我考虑过这将编写程序。当我测试这段代码时,它没有显示出问题。我进行了编辑以修复此错误。它现在保存了剩余的位。为什么要PushbackInputStream?将任何InputStream打包到InputStreamReader中(当然指定正确的编码)和一个BufferedReader。然后调用readLine()。对不起,我错过了“无换行”部分。哦,函数编程!
sed -e 's/;\?[A-Z0-9]*=/|/g' -e 's/\(^\|\)\|\(;$\)//g' < input > output