【问题标题】:Python/Django polling of database has memory leak数据库的 Python/Django 轮询存在内存泄漏
【发布时间】:2010-02-25 22:08:13
【问题描述】:

我有一个 Python 脚本运行 Django 用于数据库和内存缓存,但它特别是作为一个独立的守护进程运行(即不响应网络服务器请求)。守护程序检查 Django 模型申请中带有 status=STATUS_NEW 的对象,然后将它们标记为 STATUS_WORKING 并将它们放入队列中。

许多进程(使用多进程包创建)将从队列中拉出事物并使用传递给队列的pr.id 处理申请。我相信内存泄漏可能在以下代码中(但它可能在队列另一侧的“工人”代码中,尽管这不太可能,因为即使没有申请出现,内存大小也在增长 - 即当工作人员都在 Queue.get()) 上阻塞时。

from requisitions.models import Requisition # our Django model
from multiprocessing import Queue

while True:
    # Wait for "N"ew requisitions, then pop them into the queue.
    for pr in Requisition.objects.all().filter(status=Requisition.STATUS_NEW):
        pr.set_status(pr.STATUS_WORKING)
        pr.save()
        queue.put(pr.id)

    time.sleep(settings.DAEMON_POLL_WAIT)

settings.DAEMON_POLL_WAIT=0.01.

如果我让它运行一段时间(即几天),Python 进程将增长到无限大小,最终系统将耗尽内存。

这里发生了什么(或者我怎样才能知道),更重要的是 - 你如何运行一个守护进程来做到这一点?

我的第一个想法是改变函数的动态,特别是将新申请对象的检查放入django.core.cache cache,即

from django.core.cache import cache

while True:
    time.sleep(settings.DAEMON_POLL_WAIT)
    if cache.get('new_requisitions'):
       # Possible race condition
       cache.clear()
       process_new_requisitions(queue)

 def process_new_requisitions(queue):
    for pr in Requisition.objects.all().filter(status=Requisition.STATUS_NEW):
        pr.set_status(pr.STATUS_WORKING)
        pr.save()
        queue.put(pr.id)

使用status=STATUS_NEW 创建申请的进程可以执行cache.set('new_requisitions', 1)(或者我们可以捕获正在创建新申请的信号或 Requisition.save() 事件,然后从那里设置缓存中的标志)。

但是我不确定我在这里提出的解决方案是否解决了内存问题(这可能与垃圾收集有关 - 因此通过process_new_requisitions 进行范围界定可能会解决问题)。

感谢您的任何想法和反馈。

【问题讨论】:

  • 只是一个想法...您是否在 settings.py 中使用 DEBUG=True 运行?调试模式保存所有查询,这肯定看起来像内存泄漏:)
  • 嘿嘿。我是,确实!我忘记了。我已将其关闭(并移至缓存解决方案),内存泄漏似乎已减少。

标签: python django memory-leaks daemon


【解决方案1】:

您需要定期重置 Django 为调试目的保留的查询列表。通常在每次请求后都会清除它,但由于您的应用程序不是基于请求的,因此您需要手动执行此操作:

from django import db

db.reset_queries()

另见:

  • "Debugging Django memory leak with TrackRefs and Guppy" by Mikko 大玉:

    Django 跟踪所有查询 调试目的 (连接。查询)。这份名单是 在 HTTP 请求结束时重置。 但是在独立模式下,没有 要求。所以需要手动 每次后重置为查询列表 工作周期

  • "Why is Django leaking memory?" in Django FAQ - 两者都说 关于将DEBUG 设置为False,这始终很重要,并且 关于使用db.reset_queries() 清除查询列表, 在像您这样的应用程序中很重要。

【讨论】:

  • 这些都是可靠的参考 - 谢谢。
  • 链接好像移到了:opensourcehacker.com/2008/03/07/…
  • your application is not request based 是什么意思?就像数据库是本地的,而不是本地的?
  • @User 您可以在请求-回复周期之外使用 Diango ORM,即用于在网站中生成 HTTP 响应以外的用途。例如在一个长时间运行的脚本中。
【解决方案2】:

守护进程的settings.py文件有DEBUG = True吗?如果是这样,Django 会在内存中保存它迄今为止运行的所有 SQL 的记录,这可能会导致内存泄漏。

【讨论】:

  • 好建议!它已启用;我已禁用它。我也切换到缓存标志检查,所以它不会不断地轮询数据库。看看这两件事是否有帮助。谢谢!
【解决方案3】:

我需要处理大量数据,因此,我对这个问题的解决方案是使用多处理,并使用池来抵消正在发生的任何内存膨胀。

为了简单起见,我只是定义了一些“全局”(顶级,无论 Python 中的术语是什么)函数,而不是试图让事情变得可以腌制。

这里是抽象的形式:

import multiprocessing as mp

WORKERS = 16 # I had 7 cores, allocated 16 because processing was I/O bound

# this is a global function
def worker(params):
  # do stuff
  return something_for_the_callback_to_analyze

# this is a global function
def worker_callback(worker_return_value):
  # report stuff, or pass

# My multiprocess_launch was inside of a class
def multiprocess_launcher(params):
  # somehow define a collection
  while True:
    if len(collection) == 0:
      break
    # Take a slice
    pool_sub_batch = []
    for _ in range(WORKERS):
      if collection: # as long as there's still something in the collection
        pool_sub_batch.append( collection.pop() )
    # Start a pool, limited to the slice
    pool_size = WORKERS
    if len(pool_sub_batch) < WORKERS:
      pool_size = len(pool_sub_batch)
    pool = mp.Pool(processes=pool_size)
    for sub_batch in pool_sub_batch:
      pool.apply_async(worker, args = (sub_batch), callback = worker_callback)
    pool.close()
    pool.join()
    # Loop, more slices

【讨论】:

    【解决方案4】:

    除了 db.reset_queries() 和 DEBUG = False 技巧,这里还有另一种方法: 只需生成另一个执行 django 查询并提供队列的进程。此过程将在其自己的内存上下文中运行,并且在执行您的任务后它会释放您的内存。

    我相信有时(如果不是总是)通过执行繁重 django 事务的长时间运行进程来控制内存问题是不可避免的。

    【讨论】:

      猜你喜欢
      • 2012-11-07
      • 2019-07-18
      • 1970-01-01
      • 2014-09-22
      • 2020-07-13
      • 2012-12-02
      • 2011-12-13
      • 2014-02-26
      • 2010-11-27
      相关资源
      最近更新 更多