不能一次性INSERT几万条数据,因会触发Packet too large、内存溢出、事务日志暴涨、锁表过久等错误;分批是必须的容错手段,MySQL建议200–500行/批,PostgreSQL 500–1000行/批,SQLite 100–200行/批,需结合单行大小、字段类型、网络延迟压测确定。

为什么不能一次性 INSERT 几万条数据?
直接用 db.Exec("INSERT INTO ... VALUES (...),(...),...") 拼接几万行值,大概率触发数据库报错:MySQL 会抛 Packet too large,PostgreSQL 可能卡在参数绑定或内存溢出,SQLite 直接拒绝。更隐蔽的问题是事务日志暴涨、锁表时间过长、OOM 或连接超时。分批不是“优化”,而是必须的容错手段。
怎么确定每批多少条才安全?
没有全局最优解,取决于你的数据库类型、单行数据大小、字段类型(尤其是 TEXT/JSONB)、网络延迟和事务隔离级别。实操中建议从以下值起步并压测:
- MySQL:200–500 行/批(
max_allowed_packet默认 4MB,按平均 1KB/行反推) - PostgreSQL:500–1000 行/批(受
work_mem和 WAL 写入压力影响更大) - SQLite:100–200 行/批(单事务写入性能衰减明显)
关键原则:单批 SQL 字符串长度别超 1MB;单次事务执行时间控制在 200ms 内;避免在高并发写入时把 autocommit 关掉太久。
Go 里用 sql.Tx 分批提交的典型写法
别用 db.BeginTx(ctx, &sql.TxOptions{}) 包整个大循环——那等于没分批。正确做法是每个批次单独开事务、执行、提交/回滚,再进下一批。示例逻辑如下:
立即学习“go语言免费学习笔记(深入)”;
// 假设 data 是 []User 类型切片
const batchSize = 300
for i := 0; i < len(data); i += batchSize {
batch := data[i:min(i+batchSize, len(data))]
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err // 或记录后继续下一批?
}
stmt, err := tx.PrepareContext(ctx, "INSERT INTO users(name, email) VALUES (?, ?)")
if err != nil {
tx.Rollback()
return err
}
for _, u := range batch {
_, err := stmt.ExecContext(ctx, u.Name, u.Email)
if err != nil {
tx.Rollback()
return err // 或 log 并 continue 到下一批?
}
}
if err := tx.Commit(); err != nil {
return err
}
}
注意点:min() 需自己定义(Go 1.21+ 可用 min(i+batchSize, len(data)));PrepareContext 在事务内复用能省解析开销;但若字段太多或结构动态,改用 tx.Stmt(stmt) 复用更稳。
批量插入失败时如何定位具体哪条出错?
用 Exec 批量执行时,错误只返回一次,无法知道第几条崩了。真要精确追踪,得放弃预编译语句,改用循环 + 单条 Exec(牺牲一点性能换可观测性):
for i, u := range batch {
_, err := tx.ExecContext(ctx, "INSERT INTO users(name, email) VALUES (?, ?)", u.Name, u.Email)
if err != nil {
log.Printf("batch[%d] failed at index %d: %v", batchIndex, i, err)
tx.Rollback()
return err
}
}
或者更实用的做法:先用 Validate() 做前置校验(空值、长度、格式),再进 DB 层;数据库层统一捕获 mysql.MySQLError 或 pgconn.PgError,提取 SQLState 和 Code 做分类重试(如唯一键冲突可跳过,约束失败需告警)。
真正麻烦的从来不是“怎么分批”,而是分批后怎么让失败不中断整体流程、怎么让重试不重复写、怎么让监控看到每批耗时和成功率——这些得结合 context 超时、log field 标注 batch_id、以及外部指标上报来做。


















