应使用流式解析与批量插入:通过gin的multipart.fileheader.open()获取io.readcloser,配合csv.newreader边读边处理,避免os.readfile全量加载;postgresql优先用pgx.copyfrom,mysql则用预编译多值insert加事务控制,每批100–500行,并通过rate.limiter限速及sync.map实时反馈进度。

直接用 db.Exec 循环插 CSV 数据,在 Gin 接口里跑几百条就卡住或超时——这不是代码写得不够“完整”,而是根本没走对路径。
怎么让 Gin 接收文件并流式解析 CSV
别用 os.ReadFile 一次性加载整个文件,内存会爆。Gin 的 *multipart.FileHeader 只提供一个 Open() 方法返回 io.ReadCloser,必须配合 csv.NewReader 边读边转。
- 调用
file, err := fileHeader.Open()获取 reader,记得defer file.Close() -
csv.NewReader(file)后立刻设r.FieldsPerRecord = -1,防长字段截断 - 如果源 CSV 带 BOM(常见于 Excel 导出),先用
bytes.TrimPrefix剥掉\xef\xbb\xbf,否则第一行字段名乱码 - 字段类型不匹配(比如字符串数字列想转
int)别在csv.Read()里硬 parse,先全读成 string,校验/转换留到入库前
PostgreSQL 用户优先用 pgx.CopyFrom 而不是 db.Exec
database/sql 标准驱动没法走 COPY 协议,必须换 pgx 连接实例。它不走 SQL 解析,万行插入从 8s 降到 200ms 是常态,但要求字段顺序、类型和表结构完全对齐。
- 别复用
sql.DB,改用pgxpool.Pool或pgx.Conn;连接字符串加postgres://...前缀,不是postgresql:// -
CopyFrom第三个参数是[]string字段名列表,必须和[][]interface{}数据行顺序严格一致 - NULL 值直接传
nil,时间字段传time.Time,JSONB 字段传json.RawMessage或已序列化的[]byte - 不要手动开事务——
CopyFrom内部自动处理,出错就是整批回滚,没有“插了前 500 行成功”这种事
怎么把限速和导入进度实时透出给前端
用 rate.NewLimiter 控制吞吐,但关键是要让前端能查到“当前已导入多少、速率多少、卡在哪一行”。不能只靠日志,得存状态。
- 初始化
limiter := rate.NewLimiter(rate.Limit(100), 1),每行数据调limiter.Wait(ctx)阻塞拿令牌 - 用
sync.Map存任务 ID →struct{ Total, Done int64; LastErr string },每次成功导入后更新Done - 暴露一个
GET /import/status/:taskID接口,返回 JSON 包含progress、rps(可算Done / elapsed.Seconds())、last_error - 别等全部导入完再返回响应——Gin 接口立即返回
202 Accepted+task_id,后续轮询查状态
MySQL 怎么做等效的批量导入
MySQL 没有原生 COPY 协议支持,LOAD DATA LOCAL INFILE 是唯一接近的方案,但它依赖文件路径且服务端要开权限,生产环境常被禁用。务实做法仍是拼多值 INSERT,但必须守住几条线。
- 每批控制在 100–500 行,按字段总长度估算:比如 10 列 varchar(200),单行约 2KB,500 行 ≈ 1MB,低于 MySQL 默认
max_allowed_packet=4MB - 用
stmt, err := db.Prepare("INSERT INTO t(a,b,c) VALUES (?,?,?)")复用语句,别每次db.Exec - 整批包在
tx, _ := db.Begin()里,成功后tx.Commit(),失败tx.Rollback() - 参数切片必须平铺:3 行 × 3 列 →
[]interface{}{a1,b1,c1,a2,b2,c2,a3,b3,c3},别嵌套
最易被忽略的是错误定位——CopyFrom 或多值 INSERT 出错时,报错信息里没有行号。真要排查,得在送入前给每行加序号字段,出错后靠日志反推;或者先导进临时表,用 INSERT ... SELECT ... ON CONFLICT DO NOTHING 之类兜底,再查差异。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











