直接用bulk_create不设batch_size会触发数据库限制导致崩溃,因Django将其拼成超长单条SQL,MySQL默认max_allowed_packet=4MB、SQLite有隐式长度上限,1000条文本记录易超限报错,内存线性增长致OOM;batch_size=500–1000为安全起点,需按字段类型压测调整。

直接用 bulk_create 能把 1000 条插入从秒级压到百毫秒级,但不设 batch_size、不处理主键、不补时间字段,反而会崩得更快。
为什么不能直接 bulk_create([...]) 不加 batch_size?
不传 batch_size 参数时,Django 会把全部实例拼进一条 INSERT INTO ... VALUES (),(),()... 语句。这在 MySQL 和 SQLite 上极易触发底层限制:
-
MySQL默认max_allowed_packet=4MB,1000 条含文本字段的记录轻松超限,报错如:django.db.utils.OperationalError: (2006, "MySQL server has gone away") -
SQLite对单条 SQL 长度有隐式上限,超长直接抛sqlite3.OperationalError - 内存占用随数据量线性增长,10 万条可能吃掉几百 MB,生产环境容易 OOM
经验上,batch_size=500–1000 是安全起点;文本多或含 TextField/BinaryField 时建议降到 100–200;上线前务必用真实字段结构压测。
关联表写入失败:bulk_create 不返回主键怎么办?
bulk_create 在 MySQL/SQLite 下完全不返回主键(book.id 是 None),PostgreSQL 仅在未设 batch_size 时通过 RETURNING 返回——所以这种链式写法必然失败:
立即学习“Python免费学习笔记(深入)”;
books = Book.objects.bulk_create(book_instances) chapters = [Chapter(book_id=b.id, title='...') for b in books] # b.id 全为 None Chapter.objects.bulk_create(chapters)
可行解只有两个:
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
- 先插主表 +
ignore_conflicts=True(确保唯一约束存在),再用Book.objects.filter(title__in=[...]).values_list('id', 'title')反查 ID,靠业务字段对齐 - 手动预分配 ID:清空表后显式设
id字段(如Book(id=i+1, title=...)),适用于初始化、迁移等可控场景
别指望靠 refresh_from_db() 补 ID——它对 bulk_create 无效。
auto_now/auto_now_add 字段为空、信号不触发,怎么补?
bulk_create 完全绕过 Model.save(),所以:
-
auto_now和auto_now_add字段不会自动填充,必须显式赋值:created_at=timezone.now() -
pre_save/post_save信号完全不触发,依赖信号做审计、缓存更新的逻辑要重写 -
clean()、full_clean()校验跳过,字段合法性需在构造实例前自行校验(比如用 Pydantic 做前置解析) - 外键字段只接受 ID 数值,传
author=Author.objects.get(id=1)会报ValueError: Cannot assign "...": "Book.author" must be a "Author" instance.
简单说:bulk_create 是“裸 INSERT”,模型层所有副作用都消失了,别把它当 save() 的加速版,而是当作另一套写入通道。
ignore_conflicts 和 update_conflicts 怎么选才不翻车?
ignore_conflicts=True 会静默跳过冲突行(如唯一键重复),适合导入去重;update_conflicts(Django 4.2+)支持冲突时更新指定字段,但要注意:
-
update_conflicts要求数据库支持ON CONFLICT ... DO UPDATE(PostgreSQL OK,MySQL 需 8.0.19+ 且用ON DUPLICATE KEY UPDATE模拟) - MySQL 下
ignore_conflicts实际是INSERT IGNORE,可能掩盖本该报错的非唯一约束问题 - 两者都不触发信号、不校验字段,冲突处理逻辑要自己兜底(比如记录跳过数、落库失败明细)
真正难的不是写对参数,而是想清楚:这批数据到底要不要强一致性?如果需要精确控制每条记录的成败状态,bulk_create 就不该是第一选择——老实用 transaction.atomic 包住小批量 create(),换回可控性。


















