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>"); 

如果你的“进步”大而乱,它保存在实体(一个或更多,取决于你在做什么) 如果你正在做任务版本,我建议使用任务名称或使你的任务幂等(特别是如果你的计数的东西)。 如果您计算的东西,我建议保存包含您正在计数的实体的键的实体,并在最后计算这些。