繁体   English   中英

大型芹菜任务内存泄漏

[英]Large celery task memory leak

我有一个巨大的芹菜任务,基本上是这样工作的:

 @task
 def my_task(id):
   if settings.DEBUG:
     print "Don't run this with debug on."
     return False

   related_ids = get_related_ids(id)

   chunk_size = 500

   for i in xrange(0, len(related_ids), chunk_size):
     ids = related_ids[i:i+chunk_size]
     MyModel.objects.filter(pk__in=ids).delete()
     print_memory_usage()

我还有一个 manage.py 命令,它只运行 my_task(int(args[0])),因此它可以排队或在命令行上运行。

在命令行上运行时,print_memory_usage() 显示使用的内存量相对恒定。

在 celery 中运行时,print_memory_usage() 显示内存量不断增加,直到进程被终止(我使用的 Heroku 内存限制为 1GB,但其他主机也会​​有类似的问题。)内存泄漏似乎是与 chunk_size 对应; 如果我增加 chunk_size,每次打印的内存消耗会增加。 这似乎表明 celery 正在记录查询本身,或者我的堆栈中的其他内容。

celery 是否在其他地方记录查询?

其他注意事项:

  • 调试关闭。
  • RabbitMQ 和 Amazon 的 SQS 作为队列都会发生这种情况。
  • 这在本地和 Heroku 上都会发生(尽管由于有 16 GB 的 RAM,它不会在本地被杀死。)
  • 该任务实际上会继续做更多的事情,而不仅仅是删除对象。 稍后它通过 MyModel.objects.get_or_create() 创建新对象。 这也表现出相同的行为(内存在芹菜下增长,在 manage.py 下不增长)。

有点死尸,但这可以帮助人们在未来。 虽然最好的解决方案应该是追踪问题的根源,但有时这也是不可能的,因为问题的根源不在我们的控制范围内。 在这种情况下,您可以在生成 Celery 工作进程时使用--max-memory-per-child选项。

结果证明这与芹菜无关。 相反,是新遗物的记录器消耗了所有内存。 尽管 DEBUG 被设置为 False,它还是将每个 SQL 语句存储在内存中,以准备将其发送到他们的日志服务器。 我不知道它是否仍然以这种方式运行,但是在任务完全完成之前它不会刷新该内存。

解决方法是对每个 id 块使用子任务,对有限数量的项目进行删除。

将此作为管理命令运行时这不是问题的原因是新遗物的记录器未集成到命令框架中。

提出的其他解决方案试图减少分块操作的开销,这对 O(N) 缩放问题没有帮助,或者如果超出内存限制则强制 celery 任务失败(该功能在当时不存在)时间,但最终可能会无限重试。)

您可以使用--autoscale n,0选项运行 worker。 如果池的最小数量为 0 celery 将杀死未使用的工人并释放内存。

但这不是一个好的解决方案。

django 的收集器使用了大量内存 - 在删除之前它收集所有相关对象并首先删除它们。 您可以在模型字段上将 on_delete 设置为 SET_NULL。

另一种可能的解决方案是删除有限制的对象,例如每小时删除一些对象。 这将降低内存使用率。

Django 没有 raw_delete。 您可以为此使用原始 sql。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM