我有大约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)
但有时候这种策略也会导致内存错误,因为实例的大小可能会有很大差异,所以一个块可能会超出内存限制,而其他的则不会。
是否可以对实例列表进行分块以获得分隔大小的块?在这种情况下,最好的方法是什么?
答案 0 :(得分:7)
如果您在调试模式下使用Django,它将跟踪所有sql状态以进行调试。对于许多对象,这可能会导致内存问题。您可以使用以下命令重置它:
from django import db
db.reset_queries()
答案 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