createinbatches是gorm官方唯一推荐的批量插入方式,参数必须传值类型切片(如users),不可传[]user或&user;batchsize宜设100–500,返回gorm.db需检查result.error,不自动事务、不保证id回填。

CreateInBatches参数必须传切片,不能传指针切片或单结构体
直接传 &users(其中 users 是 []User)是常见错误——GORM v2 会 panic;正确写法是传 users 本身(值类型切片)。传 *[]User 或 &user(单个 struct)同样触发 panic。
常见误用场景:
- 从 JSON 解析后得到
*[]User,没解引用就传入 → panic - 误以为和
Create(&u)一样要取地址 → 实际CreateInBatches接收的是接口值,不是指针接收器 - 用
for _, u := range users { ptrs = append(ptrs, &u) }构造指针切片 → 所有指针指向同一栈地址,数据全变成最后一项
batchSize 设为 100–500,别设成 1 或超 1000
设成 1 会退化为逐条插入,还多一层函数调用开销;设成 1000+ 容易触达 MySQL 的 max_allowed_packet(默认 4MB),例如单行 2KB × 1000 行 ≈ 2MB,接近临界值,出错时提示 MySQL server has gone away 或 Packets larger than max_allowed_packet。
推荐实测区间:
- MySQL:200–500(平衡网络包大小与 SQL 解析开销)
- PostgreSQL:100–300(若关闭
RETURNING可放宽到 500) - SQLite:慎用 —— 底层不支持多值 INSERT,
CreateInBatches会自动降级为循环单条,此时不如直接用db.Exec拼接
错误必须从 result.Error 检查,且它只反映最后一批
CreateInBatches 返回的是 *gorm.DB,不是 error。写 if err := db.CreateInBatches(...) 直接编译失败。
正确检查方式:
result := db.CreateInBatches(users, 200)
if result.Error != nil {
// 注意:这只代表最后一批(第 N 批)失败
// 前 N−1 批已提交,不会回滚
}
想捕获每批错误?只能手动分片 + 循环调用:
- 用
chunk := users[i:i+200]切子切片 - 每批单独
tx.CreateInBatches(chunk, 200)并检查result.Error - 配合事务控制:外部用
db.Transaction(func(tx *gorm.DB) error { ... })包裹整个流程
主键回填不可靠,尤其跨批或用 MySQL 时
MySQL 不支持 RETURNING,插入后 users[0].ID 仍是 0;PostgreSQL 虽支持,但仅限单批——若你传 1000 条、batchSize=200,共 5 批,clause.Returning 结果只会填充第一批的 ID,其余批次 ID 字段保持零值。
如果你依赖插入后的 ID 做后续关联操作(比如插入 Profile 表),必须注意:
- 不要假设所有
users[i].ID在调用后都有值 - PostgreSQL 用户可关掉返回逻辑提速:
db.Session(&gorm.Session{CreateBatchSize: 200}).CreateInBatches(...) - 真需要全量 ID 回填,要么改用原生
*sql.DB+LAST_INSERT_ID()(MySQL)或pgx.CopyFrom(PostgreSQL),要么接受单批处理(牺牲吞吐换确定性)
CreateInBatches 当作一个可控的“批量执行器”,而非“全自动导入工具”,才能稳住百万级写入。











