2011-11-02 84 views
3

我需要遍歷一個非常大的文本文件,幾個千兆字節的大小(準確地說是一個區域文件)。我需要爲區域文件中的每個條目運行一些查詢,然後將結果存儲在可搜索的數據庫中。循環並處理大型(10GB +)文本文件的最佳方法是什麼?

我目前選擇的武器,主要是因爲我知道他們是Python和MySQL。然而,我不確定要麼處理這個尺寸的文件有多好。

有沒有人在這方面的經驗有任何建​​議,最好的方式來打開並通過文件循環,而不會超載我的系統?一旦我打開它(線程?)並存儲處理過的數據,處理文件的最有效方式如何?

+0

數據的格式是否像JSON一樣? – Arasu

+0

區域文件的格式與RFC 1035中規定的一樣,http://en.wikipedia.org/wiki/Zone_file – MarathonStudios

回答

4

儘管您可能無法將整個數據庫存儲在內存中,但您不應該在MySQL中存儲大量數據時遇到任何麻煩,因此可能會遇到一些IO性能問題。與往常一樣,在運行查詢之前,確保您有適當的索引。

最重要的是不要嘗試將整個文件加載到內存中。循環遍歷文件,不要嘗試使用像readlines這樣一次加載整個文件的方法。

請確保批量處理請求。一次加載幾千行,並通過一個大的SQL請求發送它們。

這種方法應該工作:

def push_batch(batch): 
    # Send a big INSERT request to MySQL 

current_batch = [] 
with open('filename') as f: 
    for line in f: 
     batch.append(line) 

     if len(current_batch) > 1000: 
      push_batch(current_batch) 
      current_batch = [] 

    push_batch(current_batch) 

區域文件是相當正常格式化,可以考慮,如果你可以只使用LOAD DATA INFILE脫身。您也可以考慮創建一個命名管道,將部分格式化的數據從python中推入,並使用LOAD DATA INFILE將其讀入到MySQL中。

MySQL有上optimizing inserts一些偉大的祕訣,一些亮點:

  • 使用在每個插入語句多個值列表。
  • 使用INSERT DELAYED,特別是如果您同時從多個客戶端推送(例如使用線程)。
  • 插入前鎖定您的表格。
  • 調整key_buffer_size和bulk_insert_buffer_size。

最快的處理將在MySQL中完成,因此請考慮一下,如果您可以在數據在數據庫之後執行所需的查詢,而不是之前。如果您確實需要在Python中進行操作,則線程無法幫助您。只有一個Python代碼線程可以一次執行(GIL),因此除非您在C中花費大量時間或者使用外部資源的接口,否則您只會在一個線程中運行無論如何。

最重要的優化問題是什麼是邊界速度,如果數據庫是邊界因素,那麼沒有意義的是旋轉一堆線程來讀取文件。真正知道的唯一方法就是嘗試一下並做出調整,直到它足夠快達到您的目的。

+0

這裏有一些非常有價值的信息Zack,謝謝!在得到解析和存儲的數據後,我將在每個條目上運行正則表達式列表並執行一些網絡請求(ping等)。我將在一個單獨的腳本中執行此操作,但我不確定Python是否會最適合這一點 - 我被告知C++會更好。 – MarathonStudios

+0

@MarathonStudios Python會很好,因爲像ping這樣的操作所花費的大部分時間都在等待網絡請求。那將是一個使用多線程的時候非常有價值的時候。創建一個要測試的區域的'threading.Queue',一個線程池將一個條目從隊列中取出,對其進行測試並存儲結果,以及一個或多個線程查詢數據庫中的幾十個尚未測試的條目並將它們添加到隊列中。調整加載和消耗線程的數量以優化性能。 –

+0

@MarathonStudios正則表達式也可以,因爲re模塊中的實際實現總是使用C語言。確保先編譯表達式(http://docs.python.org/library/re.html#re.compile),因此不必爲每個條目重做。但是請注意,MySQL不支持正則表達式(http://dev.mysql.com/doc/refman/5.1/en/regexp.html),並且在數據庫中完成的任何操作通常都會比外部程序更快(使用UPDATE語句)。 –

0

@Zack Bloom的回答非常好,我贊成它。只是一對夫婦的想法:

  • 由於他表現,只是用with open(filename) as f:/for line in f是所有你需要做的。 open()返回一個迭代器,它一次從文件中提供一行。

  • 如果你想把每一行都寫入你的數據庫,在循環中執行它。如果您只想要某些與某個正則表達式匹配的行,那很容易。

 
    import re 

    pat = re.compile(some_pattern) 

    with open(filename) as f: 
     for line in f: 
      if not pat.search(line): 
       continue 
      # do the work to insert the line here 
  • 有了一個文件,是幾個G,你很可能是I/O限制。所以可能沒有理由擔心多線程或其他任何問題。即使運行多個正則表達式,也可能比通過讀取文件或更新數據庫更快地處理數據。

  • 就個人而言,我不是一個數據庫傢伙,我喜歡使用ORM。我做了數據庫工作的最後一個項目,我用SQLite使用了Autumn。我發現ORM的默認設置是爲每個插入操作一次提交,並且插入一堆記錄需要花費很長時間,所以我擴展了Autumn以允許您用一次提交明確地包圍一堆插入;這是更快的。 (嗯,我應該延伸秋季一個Python with聲明上班,這樣就可以換了一堆插件爲with塊和秋季會自動提交。)

http://autumn-orm.org/

不管怎樣,我這一點只是對於數據庫而言,以錯誤的方式做事可能會非常緩慢。如果你發現數據庫插入是你的瓶頸,那麼你可以採取一些措施來解決它,而Zack Bloom的答案包含了幾條啓動你的想法。

相關問題