2009-05-29 38 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