如何在没有耗尽内存的情况下迭代Django中的大表?

时间:2016-08-15 16:57:42

标签: python django

我有一个Django模型,其表中有数百万条记录。我正在尝试对shell中表中的所有记录进行一些紧急维护,但是如果没有完全耗尽系统中的内存,我将无法执行MyModel.objects.all()

即使是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()在真实的应用程序中会是一个糟糕的想法,所以这个问题的范围相当有限(维护活动),但确实会不时出现。

4 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:0)

您可能无法将所有完整记录放入内存中,但很有可能您可以将所有主键放入内存中。因此,您可以迭代所有主键的列表,并在每个主键上执行bar并单独操作记录:

public static void main(String[] args) {
        LocalDate today = LocalDate.now();
        System.out.println("Current date: " + today);

        //add 1 week to the current date
        LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
        System.out.println("Next week: " + nextWeek);
    }

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

答案 2 :(得分: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,则需要使用celery创建异步任务或尝试使用某些工具来执行此操作,如Django Async

希望它有所帮助。

Take a look

答案 3 :(得分:0)

正确的答案是在迭代之前在查询集上使用Django的iterator()方法。但是,您还必须将查询包装在事务中。

    with transaction.atomic():
        for ii in MyModel.objects.all().iterator():

这是因为默认情况下,Django在“自动提交”模式下运行,这意味着数据库游标将具有WITH HOLD参数,从而导致常见的数据库(如postgres)使用较大的临时文件。