bulk_create不能直接插入百万对象,因会生成超长SQL触发数据库长度限制、内存溢出或连接超时;必须设合理batch_size(PostgreSQL≤2000、MySQL3000–5000、SQLite≤500),手动补全auto_now_add等字段,显式传外键实例,并用ignore_conflicts=True处理唯一冲突。

bulk_create 是 Django 里处理百万级插入最直接有效的手段,但直接传一百万个对象进去必崩——必须分批、补字段、绕过信号,否则不是报错就是丢数据。
bulk_create 为什么不能直接传百万对象
它底层会把所有对象拼成一条超长 INSERT 语句发给数据库。PostgreSQL 默认单条 SQL 长度上限约 1GB,MySQL 受 max_allowed_packet 限制(通常几 MB),SQLite 更小。实际中,5000 条以上就容易触发 QueryTooLarge 或内存溢出。
- 不设
batch_size时,Django 默认一次性提交全部,极易超限 - 对象列表本身占内存:100 万条
Book(title="...", price=99.99)在 Python 中约占用 300–500MB 内存 - 数据库连接可能在执行中途超时,尤其在云环境或低配服务器上
怎么安全分批:batch_size 设多少才合理
没有全局最优值,得看数据库类型和字段复杂度。实测建议:
- PostgreSQL:≤ 2000(因
RETURNING和约束检查开销大) - MySQL:3000–5000(注意调大
max_allowed_packet到 64M+) - SQLite:≤ 500(单次 INSERT 行数硬限制)
- 含 JSONField / TextField 的模型,再砍半——字段越长,单条 SQL 越容易超限
别用 list() 一次性生成全部对象再切片,改用生成器 + itertools.islice 流式处理:
立即学习“Python免费学习笔记(深入)”;
from itertools import islice <p>def bulk_import_books(data_iter, batch_size=2000): iterator = iter(data_iter) while True: batch = list(islice(iterator, batch_size)) if not batch: break Book.objects.bulk_create(batch, batch_size=batch_size)
外键、auto_now_add、default 怎么手动补
bulk_create 完全跳过模型的 save(),所以这些都不会自动发生:
-
ForeignKey字段必须传实例,不能传 ID 数字(除非你用ignore_conflicts=True且数据库有对应约束) -
auto_now_add=True的字段(如created_at)必须显式赋值:created_at=datetime.now() -
default=uuid4不会执行,得自己调:id=uuid4() -
CharField(blank=True, null=True)如果数据库实际是NOT NULL,空字符串""合法,None会报null value in column X violates not-null constraint
查真实表结构确认字段是否真允许 NULL:
\d myapp_book # PostgreSQL DESCRIBE myapp_book; # MySQL
有唯一索引冲突时怎么跳过或更新
默认遇到重复主键或唯一键会整个批次失败。解决方式取决于 Django 版本和数据库:
-
ignore_conflicts=True:仅 Django 2.2+,PostgreSQL 9.5+ / SQLite 3.24+ / MySQL 8.0.19+ 支持;MySQL 8.0.19 前不支持 -
update_conflicts=True+update_fields=[...]+unique_fields=[...]:仅 PostgreSQL 支持,用于“存在则更新”语义 - 老版本或不支持的数据库,只能先
filter().values_list('xxx', flat=True)查出已存在 key,再过滤掉重复数据
注意:ignore_conflicts 不会返回被跳过的对象,也不会抛异常,静默丢弃——得靠日志或前置校验确保逻辑正确。
真正卡住百万级导入的,往往不是速度,而是字段没补全、批次大小拍脑袋、或者外键传了 ID 却以为能自动解析。跑之前先拿 1000 条真数据走通全流程,比调参重要得多。


















