数据库的Python/Django轮询存在内存泄漏

数据库的Python/Django轮询存在内存泄漏,python,django,memory-leaks,daemon,Python,Django,Memory Leaks,Daemon,我有一个运行Django for database和memcache的Python脚本,但它作为一个独立的守护进程运行(即不响应Web服务器请求)。守护进程检查Django模型请求中状态为“status=status\u NEW”的对象,然后将其标记为“status\u WORKING”,并将其放入队列 许多进程(使用多进程包创建)将从队列中提取内容,并使用传递到队列的pr.id处理请购单。我相信内存泄漏可能存在于以下代码中(但它可能存在于队列另一侧的“Worker”代码中,尽管这不太可能,因为

我有一个运行Django for database和memcache的Python脚本,但它作为一个独立的守护进程运行(即不响应Web服务器请求)。守护进程检查Django模型请求中状态为“status=status\u NEW”的对象,然后将其标记为“status\u WORKING”,并将其放入队列

许多进程(使用多进程包创建)将从队列中提取内容,并使用传递到队列的
pr.id
处理请购单。我相信内存泄漏可能存在于以下代码中(但它可能存在于队列另一侧的“Worker”代码中,尽管这不太可能,因为即使在没有请求出现的情况下,内存大小也在增长——即当Worker都阻塞了Queue.get()时)

其中
settings.DAEMON\u POLL\u WAIT=0.01

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

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

我的第一个想法是更改函数的动态,特别是通过将对新请求对象的检查放入
django.core.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\u NEW
创建请购单的流程可以执行
cache.set('NEW\u requisions',1)
(或者,我们可以捕获正在创建新请购单的信号或Requisition.save()事件,然后在缓存中设置标志)

但是,我不确定我在这里提出的解决方案是否解决了内存问题(这可能与垃圾收集有关-因此,通过
过程\u new\u requisions
的作用域可以解决这个问题)


非常感谢您的想法和反馈。

守护进程的settings.py文件是否具有
DEBUG=True
?如果是这样的话,Django会在内存中保存一条到目前为止运行的所有SQL的记录,这可能会导致内存泄漏。

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

from django import db

db.reset_queries()

另见:

  • 米科 Ohtamaa:

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

  • -两者兼而有之 关于将
    DEBUG
    设置为
    False
    ,这一点始终很重要,以及 关于使用
    db.reset\u querys()
    清除查询列表, 在像您这样的应用程序中非常重要


除了db.reset_querys()和DEBUG=False技巧之外,还有另一种方法: 只需生成另一个执行django查询并为队列提供数据的进程。这个过程将在它自己的内存上下文中工作,在执行任务后,它将释放回您的内存


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

我有很多数据处理要做,因此,我解决这个问题的方法是使用多处理,并使用池来抵消内存膨胀

为了保持简单,我只是定义了一些“全局”(顶级,不管Python中的术语是什么)函数,而不是试图让事情变得可以pickle

这里是抽象形式:

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
将多处理导入为mp
WORKERS=16#我有7个内核,分配了16个,因为处理是I/O绑定的
#这是一个全局函数
def工作者(参数):
#做事
将某物返回给要分析的回调
#这是一个全局函数
def worker_回调(worker_返回值):
#报告材料,或通过
#我的多进程启动在一个类中
def多进程启动程序(参数):
#以某种方式定义集合
尽管如此:
如果len(集合)==0:
打破
#吃一片
池_子_批=[]
对于范围内的(工人):
如果收藏:#只要收藏中还有东西
pool\u sub\u batch.append(collection.pop())
#启动一个池,仅限于切片
池大小=工人
如果len(池子批次)<工人:
池大小=len(池子批次)
池=mp.pool(进程=池大小)
对于池子批次中的子批次:
apply\u async(worker,args=(sub\u batch),callback=worker\u callback)
pool.close()
pool.join()
#循环,更多的切片

只是一个想法。。。您是否在settings.py中使用DEBUG=True运行?调试模式保存所有查询,这看起来肯定像内存泄漏:)嘿嘿。我的确是!我忘了。我已经关闭了它(并转移到缓存解决方案),内存泄漏似乎已经减少。很好的建议!它被启用;我把它禁用了。我还切换到了缓存标志检查,因此它不会不断轮询数据库。看看这两件事是否有用。谢谢这些都是可靠的参考资料-谢谢。似乎链接已经转移到:什么意思
你的应用程序不是基于请求的
?就像数据库是本地的,vs not?@User一样,您可以在请求-应答周期之外使用Diango ORMs,即用于在网站中生成HTTP响应以外的用途。例如,在长时间运行的脚本中。
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