4

我有下面的代碼是在一個大桌子試圖環路(〜100K行;〜30GB)內存泄漏

def updateEmailsInLoop(cursor=None, stats={}): 
    BATCH_SIZE=10 
    try: 
     rawEmails, next_cursor, more = RawEmailModel.query().fetch_page(BATCH_SIZE, start_cursor=cursor) 
     for index, rawEmail in enumerate(rawEmails): 
      stats = process_stats(rawEmail, stats) 
     i = 0 
     while more and next_cursor: 
      rawEmails, next_cursor, more = RawEmailModel.query().fetch_page(BATCH_SIZE, start_cursor=next_cursor) 
      for index, rawEmail in enumerate(rawEmails): 
       stats = process_stats(rawEmail, stats) 
      i = (i + 1) %100 
      if i == 99: 
       logging.info("foobar: Finished 100 more %s", str(stats)) 
     write_stats(stats) 
    except DeadlineExceededError: 
     logging.info("foobar: Deadline exceeded") 
     for index, rawEmail in enumerate(rawEmails[index:], start=index): 
      stats = process_stats(rawEmail, stats) 
     if more and next_cursor: 
      deferred.defer(updateEmailsInLoop, cursor = next_cursor, stats=stats, _queue="adminStats") 

不過,我不斷收到以下錯誤:

在處理此請求時,處理此請求的進程被發現使用的內存過多並被終止。這很可能會導致下一個請求應用程序使用新的進程。如果您經常看到此消息,那麼您的應用程序中可能會有內存泄漏。

...有時....

超出軟私有內存128 MB與154 MB服務9個請求總

我改變了我的代碼,所以我總是隻在10後拉限在任何特定時間的參賽作品,所以我不明白爲什麼我還在用完內存?

+0

進程統計信息做什麼,可能是內存使用的來源。儘管t只能在dev中運行,但您也可能想看看Apptrace。 https://code.google.com/p/apptrace/ – 2015-05-07 11:26:13

+0

你也可以看到在離開該函數之前調用gc.collect是否會回收任何內存。 – 2015-05-07 11:27:07

回答

0

有3種方法來做這種工作(迭代對數據存儲的大型組的列):

  1. 過程1個批次X實體,並創建一個任務(按隊列)使用光標。
  2. 處理1批x實體,並使用一些javascript來響應瀏覽器,顯示進度並將window.location更改爲包含光標和當前進度的鏈接。 (這是我的首選方法)
  3. 使用映射精簡(它很難代碼)(但可以在10M-1B行被施加)

對於大多數我的應用程序,我需要此x的通常爲100-之間500。 這裏是我用於迭代超過1.5m-2m行的代碼來生成一些報告或更新我的數據庫中的東西。對於報告,我保存包含我需要的信息在csv格式的實體,並在最後,我讀取所有實體,合併它們,並刪除它們。 (這樣做產生1.5米行Excel中的數據) (它的Java,但應該很容易地轉換到Python):

resp.getWriter().println("<html><head>"); 
resp.getWriter().println(
        "<script type='text/javascript'>function f(){window.location.href='/do/convert/" + this.getClass().getSimpleName() + "?cursor=" + cursorString + "&count=" 
          + count + "';}</script>"); 
resp.getWriter().println("</head><body onload='f()'>"); 
resp.getWriter().println(
        "<a href='/do/convert/" + this.getClass().getSimpleName() + "?cursor=" + cursorString + "&count=" + count + "'>Next page -->" + cursorString + " </a>"); 
resp.getWriter().println("</body></html>"); 

如果你的「進步」大而亂,它保存在實體(一個或更多,取決於你在做什麼) 如果你正在做任務版本,我建議使用任務名稱或使你的任務冪等(特別是如果你的計數的東西)。 如果您計算的東西,我建議保存包含您正在計數的實體的鍵的實體,並在最後計算這些。