先看这段bulk_create
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from django.db import models
class DemoModel(models.Model):
name = models.CharField(max_length=255) age = models.IntegerField()
objs = [(f"name{i}", i) for i in range(1000000)]
objs_to_create = []
for obj in objs: objs_to_create.append( DemoModel(name=obj[0], age=obj[1]) )
DemoModel.objects.bulk_create(objs_to_create)
|
这段代码确实可以运行,但它存在两个严重问题:
objs_to_create
内存占用量随着对象数量和大小增加而增加,碰到大量插入数据的场景,很容易导致内存溢出。
bulk_create
方法有一个batch_size
参数,是用来支持将传入的对象分批保存的,默认为None
, 即不分批,如果对象很多,一是有可能会超出sql长度限制,二是会对数据库造成负面影响:例如内存压力,表长时间被锁定,连接超时,甚至直接挂掉等。
第二个问题好解决,直接加上batch_size参数即可。那么第一个问题呢?
答案是生成器
。我们知道python提供了yield
原语,它逐个产生值而不是一次性生成所有值保存在内存里,这会使生成器对处理大量数据时非常高效。
接下来是优化后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| from itertools import islice
from django.db import models
class DemoModel(models.Model):
name = models.CharField(max_length=255) age = models.IntegerField()
objs = [(f"name{i}", i) for i in range(1000000)]
def obj_generator(objs): for obj in objs: yield DemoModel(name=obj[0], age=obj[1])
objs_to_create = obj_generator(objs)
batch_size = 500 while True: batch = list(islice(objs_to_create, batch_size)) if not batch: break DemoModel.objects.bulk_create(batch, batch_size)
|