Django - bulk_create()导致内存错误

时间:2015-03-16 17:01:01

标签: 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)

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

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

4 个答案:

答案 0 :(得分:7)

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

from django import db
db.reset_queries()

请参阅why-is-django-leaking-memory

答案 1 :(得分:4)

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

Syntax: bulk_create(objs, batch_size=None)
Feature.objects.bulk_create(instances, batch_size=1000)

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

答案 2 :(得分:2)

我遇到了同样的问题并最终得到了这个解决方案:

class BulkCreateManager(object):

    model = None
    chunk_size = None
    instances = None

    def __init__(self, model, chunk_size=None, *args):
        self.model = model
        self.chunk_size = chunk_size
        self.instances = []

    def append(self, instance):
        if self.chunk_size and len(self.instances) > self.chunk_size:
            self.create()
            self.instances = []

        self.instances.append(instance)

    def create(self):
        self.model.objects.bulk_create(self.instances)


instances = BulkCreateManager(Model, 23000)
for host in hosts:
    instance = ...
    instances.append(instance)

instances.create()

答案 3 :(得分:2)

如果您没有以DEBUG模式运行,但是仍然有错误,我的解决方案应该可以为您提供帮助。首先,请确保您有一组懒散生成的对象要保存(例如,从远程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

接下来,我们需要一个函数,该函数最多一次从该生成器中获取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

并像这样使用它

print(bulk_create_iter(data_gen))
# prints 100000

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

该函数的通用版本已准备好用作Django Manager类的一种方法(您可以通过编写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