必须用 db.begin() 显式开启事务批量插入,避免默认自动提交导致万次磁盘刷写;postgresql 优先用 pgx.copyfrom(快5–10倍),mysql 需手动拼接多值 insert 并安全转义。

用 sql.DB 批量插入前必须关掉自动提交
Go 默认每条 Exec 都走一次事务提交,万级数据会变成万次 round-trip 和磁盘刷写,性能直接崩到个位数 QPS。不是慢在 Go,是慢在数据库反复开合事务。
- 务必用
db.Begin()显式开启事务,所有stmt.Exec()在其内完成,最后统一tx.Commit() - 别依赖
defer tx.Rollback()就完事——出错时要判断是否已 commit,否则可能 panic - PostgreSQL 要注意:如果用
COPY FROM STDIN(见下节),事务仍是必需的,但底层不走 SQL 解析,快一个数量级
PostgreSQL 用 pgx.CopyFrom 比原生 Exec 快 5–10 倍
原生 database/sql 对批量没优化,而 pgx 的 CopyFrom 直接对接 PostgreSQL 的二进制 COPY 协议,跳过 SQL 解析、类型转换和单行绑定开销。
- 需要把数据提前转成
[]interface{}切片(每行一维),再拼成二维切片传入CopyFrom - 列顺序必须和目标表字段严格一致,且类型要匹配——
int64不能传int,否则报cannot convert int to int64 - 别在循环里反复调
CopyFrom,每次调用都含连接协商开销;按 1000–5000 行/批分组,一批一调
_, err := conn.CopyFrom(ctx, pgx.Identifier{"users"}, []string{"id", "name", "email"},
pgx.CopyFromRows(data))
MySQL 批量插入得靠 INSERT INTO ... VALUES (),(),() 拼接
MySQL 没 COPY 协议,database/sql 又不支持原生多值 INSERT 的参数绑定,只能手动拼 SQL——安全边界全靠开发者守。
- 单条语句最多插 1000–2000 行,再大 MySQL 会报
packets larger than max_allowed_packet - 字符串值必须用
sql.EscapeString()或mysql.Escape()(取决于驱动)转义,否则注入风险真实存在 - 别用
fmt.Sprintf拼整条 SQL——数值类型容易溢出或格式错乱,优先用strconv+ 显式类型断言
内存和错误处理比速度更早卡住你
万级数据不是“插完就完”,中间任何一步失败都会让整个批次悬空:内存暴涨、连接卡死、事务堆积、主从延迟飙升。
- 读 CSV/JSON 时别
ioutil.ReadFile一把全读进内存——用bufio.Scanner或流式 JSON 解码器逐行处理 - 每批执行后检查
err,但别一错就全退;记录失败行号+原始数据到本地文件,供后续重试 - PostgreSQL 的
CopyFrom出错时不会告诉你哪一行错,只报“copy failed at row N”,所以入库前自己做基础校验(非空、长度、格式)
真正卡住的往往不是语法或函数怎么写,而是忘了设 context.WithTimeout,结果某批卡住十分钟,整个导入流程僵死在那里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











