6

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

 @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()

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

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

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

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

其他注意事项:

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

4 回答 4

1

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

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

将其作为管理命令运行时这不是问题的原因是 new relic 的记录器没有集成到命令框架中。

提出的其他解决方案试图减少分块操作的开销,这无助于 O(N) 缩放问题,或者如果超出内存限制(该功能在时间,但最终可能会无限重试。)

于 2019-11-05T15:51:21.310 回答
1

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

于 2019-11-04T14:28:26.263 回答
0

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

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

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

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

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

于 2015-04-03T11:04:37.000 回答
0

尝试使用 @shared_task 装饰器

于 2014-02-13T09:54:32.337 回答