批量insert比循环单条快5–20倍,因减少网络往返、日志写入和sql解析;需控制每批100–500行防max_allowed_packet超限;预编译+动态占位符防注入;mysql?数超65535需分批;upsert支持但需注意冲突判定与字段引用限制。

为什么 INSERT INTO ... VALUES (...), (...), (...) 比循环单条 Exec 快得多
因为单条 Exec 每次都触发一次网络往返(即使本地数据库)、一次事务日志写入、一次 SQL 解析开销;而批量拼成一条语句,这些开销只发生一次。实测在 MySQL 上插入 1000 行,批量方式通常比循环快 5–20 倍,具体取决于网络延迟和行大小。
但注意:不能无限制拼接。MySQL 默认 max_allowed_packet 是 4MB,超出会报错 MySQL server has gone away;PostgreSQL 对单条语句长度也有隐式限制。建议单批控制在 100–500 行之间。
用 sql.DB.Prepare + 可变参数实现安全批量插入
手动拼接 SQL 容易出 SQL 注入或类型错误,正确做法是复用预编译语句,动态生成占位符。例如插入用户表:
// 构建占位符:(?, ?, ?), (?, ?, ?), ...
placeholders := make([]string, len(users))
args := make([]interface{}, 0, len(users)*3)
for i, u := range users {
placeholders[i] = "(?, ?, ?)"
args = append(args, u.Name, u.Email, u.Age)
}
sql := "INSERT INTO users (name, email, age) VALUES " + strings.Join(placeholders, ", ")
<p>stmt, <em> := db.Prepare(sql)
defer stmt.Close()
</em>, _ := stmt.Exec(args...)</p>
关键点:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
args...展开必须与?数量严格一致,少一个或多一个都会导致sql: expected X arguments, got Y - 所有字段类型需提前对齐,比如
int64和int混用可能触发驱动内部转换失败 - PostgreSQL 需用
$1, $2, $3占位符,不能用?;可统一用database/sql的driver.NamedValue或改用支持位置绑定的驱动(如pgx)
遇到 ERROR 1390 (HY000): Prepared statement contains too many placeholders 怎么办
这是 MySQL 5.7+ 对单条预编译语句中 ? 数量的硬限制(默认 65535),不是内存或包大小问题。例如插入 1000 行、每行 10 字段,就用了 10000 个 ? —— 看似安全,但若字段更多或批次更大,很容易触顶。
解决方法只有分批:
- 把原始数据切片,每批不超过 5000 个占位符(留余量)
- 不要依赖
len(data) * fieldCount 就认为安全,因为某些驱动会在内部额外加占位符(如 <code>ON DUPLICATE KEY UPDATE场景) - 用
for i := 0; i 手动分片,别用 <code>chunkBy类工具函数——它们往往忽略末尾不足整批的情况
要不要用 ON DUPLICATE KEY UPDATE 或 UPSERT 批量去重
可以,但要注意语义差异。MySQL 的 ON DUPLICATE KEY UPDATE 在批量语句中对每一行独立判断冲突并更新,行为符合直觉;PostgreSQL 的 ON CONFLICT DO UPDATE 也类似。但陷阱在于:
- 主键或唯一索引必须明确,否则冲突检测失效,变成纯插入或全报错
- 更新子句里不能引用未出现在
VALUES中的列,例如UPDATE name = VALUES(name), updated_at = NOW()合法,但updated_at = updated_at + 1不合法(updated_at不在当前行VALUES里) - 如果批量中某几行冲突、某几行不冲突,整个语句仍成功执行,不会中断,但你要靠
sql.Result.RowsAffected()判断实际插入/更新数——它返回的是“变更行数”,不是“插入行数”
真正难处理的是部分失败场景:比如 100 行里第 50 行因违反外键约束失败,整个语句回滚。这时只能降级为小批量 + 事务捕获具体错误行。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










