
本文详解 go 使用 go-sql-driver/mysql 批量插入时卡在 150 行的常见原因,包括误用 query、连接池配置不当、未处理错误等,并提供基于 prepare + exec 的高性能、健壮的批量插入方案。
本文详解 go 使用 go-sql-driver/mysql 批量插入时卡在 150 行的常见原因,包括误用 query、连接池配置不当、未处理错误等,并提供基于 prepare + exec 的高性能、健壮的批量插入方案。
你遇到的 “Can't insert more than 150 rows” 并非 MySQL 硬性限制,而是典型资源耗尽或逻辑误用的表现。根本原因通常有三点:
✅ 错误地使用 Query 执行 INSERT —— Query 专为返回结果集(如 SELECT)设计,而 INSERT 应统一使用 Exec;
✅ 未复用连接与语句 —— 每次调用 Query 或 Exec 若未预编译,会重复解析 SQL、建立临时连接,快速耗尽连接池或触发 MySQL max_connections 限制;
✅ 忽略错误且未配置连接池 —— 错误被静默丢弃,导致无法定位真实问题(如 ERROR 1040: Too many connections),同时默认连接池参数(MaxOpenConns=0 即不限制,但实际受系统/MySQL 限制)易引发瓶颈。
✅ 正确做法:Prepare + Exec + 连接池优化
// 初始化数据库连接池(推荐在应用启动时一次性配置)
db, err := sql.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/dbname?parseTime=true")
if err != nil {
log.Fatal("failed to open db:", err)
}
// 设置合理连接池参数(根据负载调整)
db.SetMaxOpenConns(20) // 最大并发连接数(避免耗尽 MySQL max_connections)
db.SetMaxIdleConns(10) // 空闲连接数(减少频繁创建销毁开销)
db.SetConnMaxLifetime(30 * time.Minute) // 连接最大存活时间,防长连接失效
// 预编译 INSERT 语句(复用同一 Stmt,显著提升性能)
stmt, err := db.Prepare("INSERT INTO `movies` (`name`, `year`, `release_date`, `full_plot`, `genre`, `imdb_id`) VALUES (?, ?, ?, ?, ?, ?)")
if err != nil {
log.Fatal("failed to prepare statement:", err)
}
defer stmt.Close() // 注意:仅在不再需要该 Stmt 时关闭(如服务退出前)
// 批量插入单条记录(可安全并发调用)
insertMovie := func(movie Movie) error {
_, err := stmt.Exec(movie.Name, movie.Year, movie.ReleaseDate, movie.FullPlot, movie.Genre, movie.IMDBID)
if err != nil {
return fmt.Errorf("failed to insert movie %s: %w", movie.Name, err)
}
return nil
}
// 示例:插入多部电影(支持 goroutine 并发,因 stmt 是线程安全的)
var wg sync.WaitGroup
for _, m := range movies {
wg.Add(1)
go func(movie Movie) {
defer wg.Done()
if err := insertMovie(movie); err != nil {
log.Printf("Insert error: %v", err) // 不再静默丢弃!
}
}(m)
}
wg.Wait()
⚠️ 关键注意事项
- 永远不要忽略 error:err != nil 必须显式检查并记录/上报,否则无法发现 ERROR 1040(连接超限)、ERROR 1205(死锁)等关键异常;
- 避免在循环内反复 Prepare:每次 Prepare 会产生独立的服务器端预处理语句,浪费资源;应全局复用一个 *sql.Stmt;
- 慎用 Query 插入:Query 会隐式期待结果集,MySQL 可能分配额外内存并维持游标,长期运行易泄漏;Exec 专为无返回值操作设计,更轻量;
- 监控连接状态:可通过 db.Stats() 查看当前 OpenConnections、InUse, Idle 等指标,辅助调优 SetMaxOpenConns;
- 考虑批量 INSERT(可选进阶):若单条插入仍慢,可合并为 INSERT INTO ... VALUES (...), (...), (...),但需注意单条 SQL 长度限制(max_allowed_packet)和事务原子性权衡。
通过以上重构,你的插入吞吐量将显著提升,轻松突破 150 行瓶颈,并具备生产环境所需的稳定性与可观测性。











