最稳妥的批量插入方式是使用 db.CreateInBatches 并置于事务中,合理设置 batchSize(建议100~500),确保结构体含正确 GORM 标签,避免手写循环或裸拼 SQL;重复键处理优先用原生 ON DUPLICATE KEY UPDATE 等语句,并做好错误定位与事务回滚。

iris 中用 db.CreateInBatches 批量插入最稳妥
直接调 CreateInBatches 是 Iris 生态(配合 GORM)下批量插入的首选,它底层会自动分片、拼接 SQL,避免单条 INSERT 的性能灾难。别手写循环调 Create,那等于放弃批量意义。
常见错误是传错第二个参数:batchSize 不是“总共插多少”,而是“每批多少条”。设太小(如 10)仍会发太多请求;设太大(如 10000)可能触发 MySQL max_allowed_packet 限制或内存压力。
-
batchSize建议从 100~500 起步,根据单条记录大小和数据库配置调整 - 确保结构体已绑定
gorm标签,否则字段映射失败,插进去全是零值或 NULL - 若用
map[string]interface{}批量插,GORM 不支持直接CreateInBatches,得先转成结构体切片或改用原生 SQL
手动拼接 SQL 插入时必须防 SQL 注入
绕过 GORM、用 db.Exec 拼 INSERT INTO ... VALUES (...), (...) 确实更快,但风险极高——任何用户输入未经处理就进 SQL 字符串,立刻中招。
正确做法:用 sql.Named 或 GORM 的 db.Session(&session).Exec 配合命名参数,让驱动做参数绑定。别用 fmt.Sprintf 拼接值。
- 字符串值必须用
strconv.Quote或交给驱动处理,不能裸写"'"+name+"'" - 时间类型注意时区,
time.Now()直接拼字符串可能被 MySQL 按系统时区解析错 - 整数、布尔等基础类型看似安全,但若来源不可信(比如前端传来的 ID),仍建议走参数化
用 ON DUPLICATE KEY UPDATE 处理重复主键/唯一键
批量导入常遇到“有则更新、无则插入”的场景,GORM 的 Clauses(clause.OnConflict{...}) 在 1.24+ 支持,但 Iris 默认集成的 GORM 版本可能较旧,容易报 undefined: clause。
更兼容的做法是直接在 db.Exec 里写原生语句,MySQL 用 ON DUPLICATE KEY UPDATE,PostgreSQL 用 ON CONFLICT DO UPDATE,SQLite 用 ON CONFLICT REPLACE。
- MySQL 示例:
INSERT INTO users (id,name,age) VALUES (?, ?, ?), (?, ?, ?) ON DUPLICATE KEY UPDATE name=VALUES(name), age=VALUES(age) - 务必确认表上有
UNIQUE或PRIMARY KEY约束,否则ON DUPLICATE KEY不生效 - 更新字段不能写
id=VALUES(id),主键冲突时它本来就不会变
事务包裹 + 错误检查不能省
批量操作一旦出错,默认是部分成功、部分失败,很难回滚干净。必须显式开启事务,并在 defer tx.Rollback() 后检查所有步骤是否完成。
特别注意:GORM 的 CreateInBatches 即使在事务内,某一批失败也不会自动回滚前面成功的批次——它只保证单次调用的原子性,不保证整个切片的原子性。
- 用
tx.CreateInBatches替代db.CreateInBatches,确保全部在同一个事务里 - 执行后必须检查
tx.Error,不能只看len(results)是否匹配预期数量 - 大批次插入前加
tx.Session(&gorm.Session{PrepareStmt: true}),可复用预编译语句,提升性能
真正难的不是语法,而是判断哪部分该用 GORM 封装、哪部分该切到原生 SQL;还有就是批量失败时,日志里得能快速定位是第几批、哪条数据触发了约束冲突或类型转换错误——这些往往比插入本身花更多调试时间。


















