Django - bulk_create()导致内存错误

Bel*_*dar 5 python django postgresql

我有大约40万个对象实例插入postgres.我正在使用bulk_create()来执行此操作,但我遇到了内存错误.

我的第一个想法是将实例列表分块:

def chunks(l, n):
    n = max(1, n)
    return [l[i:i + n] for i in range(0, len(l), n)]

for c in chunks(instances, 1000):
    Feature.objects.bulk_create(c)
Run Code Online (Sandbox Code Playgroud)

但有时候这种策略也会导致内存错误,因为实例的大小可能会有很大变化,因此一个块可能会超出内存限制,而其他的则不会.

是否可以将实例列表分块以获得分隔大小的块?在这种情况下,最好的方法是什么?

lin*_*nqu 11

如果您在调试模式下使用Django,它将跟踪所有sql状态,以进行调试.对于许多对象,这可能会导致内存问题.你可以用以下方法重置:

from django import db
db.reset_queries()
Run Code Online (Sandbox Code Playgroud)

看看为什么是django-leaking-memory

  • 绝对是这个。如果您有大量查询和/或通过 SQL 的大型数据集,您真的必须小心使用“DEBUG=True”。即使它不是您*现在*遇到的具体问题,如果您不认真对待它,您也可能会遇到它。 (2认同)

Val*_*lar 6

如果您没有以调试模式运行,并且仍然有错误,我的解决方案应该可以帮助您。首先,确保您有一组要保存的延迟生成的对象(例如,批量从远程 API 获取)

def generate_data():
    """Example data generator"""
    for i in range(100000):
        yield Model(counter=i)

data_gen = generate_data()
# >> print data_gen
# <generator object data at 0x7f057591c5c8>
# 
# it's a generator, objects are not yet created. 
# You can iterate it one-by-one or force generation using list(data_gen)
# But for our approach, we need generator version
Run Code Online (Sandbox Code Playgroud)

接下来,我们需要一个函数,它一次从该生成器中获取最多 X 个对象并使用 保存它batch_create。这样,在某一时刻我们将在内存中保存不超过 X 个对象。

from itertools import islice

def bulk_create_iter(iterable, batch_size=10000):
    """Bulk create supporting generators. Returns only count of created objects."""
    created = 0
    while True:
        objects = Model.bulk_create(islice(iterable, batch_size))
        created += len(objects)
        if not objects:
            break
    return created
Run Code Online (Sandbox Code Playgroud)

并像这样使用它

print(bulk_create_iter(data_gen))
# prints 100000
Run Code Online (Sandbox Code Playgroud)

不能直接使用的原因batch_create是它在内部正在执行list(objs),因此整个生成器被实例化并保存到内存中。在这种方法中,我们batch_size一次实例化最多的对象。此方法甚至可以用于处理非常大的集合,因为内存消耗应该是恒定的(使用 15 000 000 条记录进行测试,内存使用量始终低于 300MB)。

准备使用此函数的通用版本,作为 DjangoManager类的方法(您可以通过编写在模型中使用它objects = BulkManager()):

from itertools import islice
from django.db import models

class BulkManager(models.Manager):

    def bulk_create_iter(self, iterable, batch_size=10000):
        """Bulk create supporting generators, returns number of created objects."""
        created = 0
        while True:
            objects = self.bulk_create(islice(iterable, batch_size))
            created += len(objects)
            if not objects:
                break
        return created
Run Code Online (Sandbox Code Playgroud)


Du *_* D. 5

您可以在bulk_create方法中指定batch_size。

Syntax: bulk_create(objs, batch_size=None)
Feature.objects.bulk_create(instances, batch_size=1000)
Run Code Online (Sandbox Code Playgroud)

https://docs.djangoproject.com/zh-CN/1.7/ref/models/querysets/#bulk-create