因为createinbatches将多条记录合并为单条insert语句执行,减少网络往返、事务开销和日志刷盘;而循环create是n次独立sql操作,性能损耗倍增。

gorm.DB.CreateInBatches 为什么比循环 Create 快得多
因为 CreateInBatches 把多条记录合成一条 SQL INSERT INTO ... VALUES (...), (...), (...) 执行,而循环调用 Create 是 N 次独立事务或 N 条单独 INSERT,网络往返、事务开销、日志刷盘全翻倍。
实操建议:
- 必须显式传入
batchSize,比如db.CreateInBatches(&users, 100);不设值不会自动分批,而是全塞进一条 SQL(可能超 MySQL max_allowed_packet) - 所有记录必须是同一结构体类型,且非零字段语义一致;混入
nil时间或零值 ID 可能导致部分行插入失败但无报错 - 该方法不触发单条记录的
BeforeCreate钩子——每个元素的钩子函数根本不会被调用,需要预处理就别指望它 - PostgreSQL 对单条 INSERT 的行数限制更严,
batchSize建议控制在 50–100;MySQL 可稍高,但超过 1000 易触发Packets larger than max_allowed_packet
批量插入时主键 ID 没回填?这是设计使然
CreateInBatches 默认不返回生成的主键——哪怕数据库支持 RETURNING(如 PostgreSQL),GORM 也跳过了结果扫描。这不是 bug,是为兼容 SQLite 和性能做的取舍。
如果确实需要 ID 回填:
- PostgreSQL 用户可改用
db.Clauses(clause.Returning{Columns: []clause.Column{{Name: "id"}}}).CreateInBatches(...),但注意这会降低兼容性,SQLite 直接 panic - MySQL 用户基本没辙,
LAST_INSERT_ID()只能拿到第一条的 ID,后续无法映射;老老实实用循环 +Create,或自己拼INSERT ... VALUES+exec.Query - 更稳的路:插入前用 UUID 或 Snowflake 生成 ID,彻底绕开数据库自增回填问题
用 Session 控制事务和上下文时的常见翻车点
很多人想在事务里做批量插入,顺手写 db.Session(&session).CreateInBatches(...),结果发现事务没生效、Context 超时没传递、甚至连接池被占满。
关键原因:
-
CreateInBatches内部会主动调用db.Statement.ConnPool.Prepare,若 session 带了Context但未设置超时,Prepare 阶段卡住会导致整个连接挂起 - 事务 session 必须带
NewDB: true,否则复用原 db 实例,Commit()时可能误提交其他 goroutine 的操作 - 别在循环里反复
db.Session(...).CreateInBatches——每次 Session 都新建 statement,大量临时对象逃逸到堆,GC 压力陡增
替代方案:原生 Exec + Scan 比 GORM 更可控
当数据量大、字段动态、或需精确控制返回值时,硬上 GORM 反而添乱。直接拼 SQL + db.Exec 或 db.Raw().Scan() 更干净。
例如 PostgreSQL 批量 upsert:
sql := `INSERT INTO users (name, email) VALUES $1, $2 ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name`
db.Exec(sql, pgx.NamedArgs{"name": "a", "email": "a@b.c"}, pgx.NamedArgs{"name": "b", "email": "b@b.c"})
注意:
- MySQL 用
ON DUPLICATE KEY UPDATE,SQLi 风险必须用参数化,别拼字符串 -
db.Raw().Scan()能拿回RETURNING结果,但结构体字段名必须和 SQL 列名严格匹配(大小写敏感) - 这种写法绕过 GORM 的 Model 验证和钩子,数据一致性得自己兜底
批量这事,越靠近数据库原语,越少意外;越依赖 ORM 封装,越容易在边界 case 里掉坑。特别是并发写、字段策略不统一、或要跨表关联生成 ID 的场景,提前想好退路比强行套 API 强。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











