2009-05-29 40 views
4

我需要解析非常大的日志文件(> 1Gb,< 5Gb) - 实际上我需要将数据剥离到对象中,以便将它们存储在数据库中。日志文件是连续的(没有换行符),如:解析真的很大的日志文件(> 1Gb,<5Gb)

TIMESTAMP = 20090101000000; PARAM1 = Value11; PARAM2 = Value21; PARAM3 = Value31; TIMESTAMP = 20090101000100; PARAM1 = Value11; PARAM2 = Value21; PARAM3 = Value31; TIMESTAMP = 20090101000152; PARAM1 = Value11; PARAM2 = Value21;参数3 = Value31; ...

我需要剥离到表这样的:

TIMESTAMP | PARAM1 | PARAM2 | PARAM3

该过程需要尽可能快。我正在考虑使用Perl,但任何使用C/C++的建议都会非常受欢迎。有任何想法吗?

最好的问候,

亚瑟

+0

我添加了一个工作脚本了这个问题。 – 2009-05-29 21:50:20

回答

11

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

+0

不需要深奥的解决方案。 5GB不是那么大。 SATA2是300 MB /秒,所以它应该需要大约20秒。 – ebo 2009-05-29 20:59:00

+1

当然,如果你只想这样做一次。 – Dave 2009-05-29 21:00:45

+0

@ebo:300mbps数字是理论上的最大值。如果没有任何争用,典型的硬盘实际上可以达到75-150mbps。如果你正确地设置了一个RAID阵列,那么你实际上可能会遇到接口带宽限制。 – 2009-05-29 23:33:16

5

Lex处理这种事情得非常好。

+2

我喜欢它,当有人使用工具*好*为它想要做的事情。 – BCS 2009-05-29 22:22:59

0

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

3

但是真的使用AWK。它的性能并不差,甚至可以和Perl比较。Map/Reduce的源代码可以很好地工作,但是将文件拆分成合适的块的开销又如何呢?

尝试AWK

+0

这将需要使用awk的RS变量(=〜Perl's $ /),因为该文件不包含换行符。考虑到“man awk”在这里说“RS是一个正则表达式(当不是单个字符时)”,它对性能有什么影响? – 2009-05-29 21:32:33

0

如果你编写自己的解决方案,你可能会从文件中读取更大的数据块,并分批处理它们(而不是使用,也就是说,readline()),并寻找新行标中受益每行的结尾。通过这种方法,您需要注意您可能没有检索到最后一行的全部内容,因此需要一些逻辑来处理它。

我不知道你会发现什么样的性能好处,因为我没有测试过它,但我已经利用了类似的技术取得了成功。

+0

没有换行... – BCS 2009-05-29 22:24:07

3

关键不是语言,因为问题是I/O限制,所以选择您感觉最舒适的语言。

关键是它是如何编码的。只要不将整个文件加载到内存中,您一切都会好起来的 - 一次加载块,并且一次保存数据块,效率会更高。

Java有一个PushbackInputStream,可以使这个代码更容易。这个想法是,你猜想你有多少阅读,如果你读得太少,那么把数据推回去,然后读一个更大的块。

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

+0

为什么PushbackInputStream?在InputStreamReader中包装任何InputStream(指定正确的编码当然)和一个BufferedReader。然后调用readLine()。 – 2009-05-30 10:11:55

3

像这样的东西应该工作。

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; 
    } 
} 
1

这是很容易在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); 
} 
0

我知道这是一个奇特的语言,可能不是最好的解决方案要做到这一点,但是当我即席数据,我认为PADS

7

此演示文稿有关使用Python生成器的吹我的脑海里: http://www.dabeaz.com/generators-uk/

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'] 

他还表明,性能媲美于像grep,find等标准unix工具的性能。 当然这是Python,它比Perl或awk脚本更容易理解,最重要的是更容易定制或适应不同的问题集。

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

1

听起来像是sed工作:

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