2016-08-15 65 views
0

我有一个Django模型,其表中有数百万条记录。我正在尝试对外壳中的表中的所有记录执行一些紧急维护,但是我无法在不完全耗尽系统内存的情况下执行MyModel.objects.all()如何迭代Django中的大型表而不会耗尽内存?

即使pass导致OOM杀手被称为,杀死我的过程:

for ii in MyModel.objects.all(): 
    pass 

的原因是因为Django的QuerySet正试图建立自己的“结果缓存”,以建立一个列表与所有我在它的记录,这里

# django/db/models/query.py 
def _fetch_all(self): 
    if self._result_cache is None: 
     self._result_cache = list(self.iterator()) # <<<< this guy! 
    if self._prefetch_related_lookups and not self._prefetch_done: 
     self._prefetch_related_objects() 

但是我的机器不能保留在内存中的整个列表。

当然,在如此大的表格上迭代.all()在真实应用程序中会是一个糟糕的主意,所以这个问题的范围相当有限(维护活动),但它确实时常出现。

回答

3

首先要尝试使用的查询集的iterator()方法迭代之前:

for ii in MyModel.objects.all().iterator(): 
+0

嗯,这确实出现了绕过结果缓存,但我仍然(在'Django的/ DB /后端/ utils.py'在'CursorWrapper.execute')运行内存.. 。所以我猜结果缓存不是唯一让我难过的东西,它只是从postgres获取的大量记录... – mgalgs

+0

令我感到惊讶,但我还没有深入到Django使用的细节游标与将记录拉入内存的位置。如果您可以在没有实际模型实例的情况下进行维护,或者愿意将原始数据库记录转换为模型,那么从这个答案中链接的博客帖子可能会有帮助吗? HTTP://计算器。com/questions/18381695/get-database-cursor-from-djangos-rawqueryset https://docs.djangoproject.com/en/1.10/topics/db/sql/#connections-and-cursors为Django文档。 –

+0

我在模型实例上调用方法,但我完全可以将这些工作分解出来,这是我希望避免的一些工作。这里和那里的奇怪维护并不是什么大不了的事情。我只是希望有一个很好的方法可以做到这一点,而不必下降到原始数据库记录。好吧。 – mgalgs

0

您可能无法将所有完整记录放入内存中,但很有可能您可以将所有主键都装入内存中。所以,你可以遍历所有的主键的列表,并做每一个.get()并在记录上独立地操作:

for pk in MyModel.objects.values_list('pk', flat=True): 
    ii = MyModel.objects.get(pk=pk) 
    ii.maintenance_activity() 

这是不是超级CPU效率,当然,但它更多的内存效率。鉴于这类事情只应用于维护活动,理想的性能不应该成为问题。

0

如果您正在使用python3.X你可以尝试一些异步任务。

创建计划提取可能很有用。

事情是这样的:

async def _fetch_all(self): 
    if self._result_cache is None: 
      self._result_cache = await list(self.iterator()) # <<<< this guy! 
    if self._prefetch_related_lookups and not self._prefetch_done: 
      await self._prefetch_related_objects() 

要运行代码:

import asyncio 
my_model = MyModel() 
asyncio.get_event_loop().run_until_complete(my_model._fetch_all()) 

但如果你使用的是2.7,你将需要创建芹菜一个异步任务,或尝试一些工具来做到这一点如Django Async

希望它有帮助。

Take a look