mysql批量插入应使用insert into ... values (...), (...)单语句多值形式,避免循环单条执行;需控制单次行数(建议≤1000)、显式事务(≤5000行)、防sql注入(用?占位)、合理处理主键冲突(优先on duplicate key update)。

MySQL批量插入用INSERT INTO ... VALUES (...), (...),别用循环单条
Go里最常见性能陷阱就是对每条数据调用一次db.Exec——网络往返+事务开销直接拖垮吞吐。正确做法是拼成一条多值INSERT语句,让MySQL一次解析、一次写入。
注意不是所有场景都适合无限拼接:MySQL默认max_allowed_packet约4MB,超长SQL会报错MySQL server has gone away。实际建议单次不超过1000行(视单行数据大小动态调整)。
- 用
strings.Builder拼SQL,避免+=字符串反复分配 - 参数必须用
?占位符,不要字符串插值,防SQL注入 - 值列表末尾不加逗号,拼接逻辑要处理好边界
用sqlx.In或database/sql原生方式处理参数绑定
手动拼VALUES (?, ?), (?, ?)容易出错,尤其参数数量和顺序对不上时。推荐两种稳妥方案:
- 如果项目已用
sqlx:先调sqlx.In生成带?的SQL和展开后的参数切片,再用db.MustExec执行 - 纯
database/sql:自己写个辅助函数,把[]interface{}按每批N个分组,每组生成对应数量的?,再append所有参数到一个大切片
别直接把结构体切片传给Exec——database/sql不支持自动解构,会报unsupported type []MyStruct。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
事务控制必须显式开启,否则批量没意义
即使用了批量SQL,如果每条还走自动提交,性能提升有限。MySQL在事务内批量写入才能真正减少日志刷盘和索引更新次数。
- 用
tx, err := db.Begin()开始,成功后tx.Commit(),失败tx.Rollback() - 别依赖
defer tx.Rollback()——它会在函数返回时执行,可能掩盖真实错误 - 大事务有风险:锁持有时间变长,可能阻塞其他查询;崩溃恢复也更慢。建议单次事务控制在5000行以内
错误现象:ERROR 1205 (HY000): Deadlock found when trying to get lock,往往就是事务太大、锁范围太广导致的。
注意ON DUPLICATE KEY UPDATE和REPLACE INTO的语义差异
需要去重插入时,很多人直接上REPLACE INTO,但它本质是DELETE + INSERT,会引发自增ID跳变、触发器重复执行、外键约束检查两次等问题。
- 优先用
INSERT ... ON DUPLICATE KEY UPDATE,只更新指定字段,行为可控 - 多个唯一键冲突时,
ON DUPLICATE KEY UPDATE只触发一次,而REPLACE可能删多条 - 如果表有
ON UPDATE CURRENT_TIMESTAMP字段,ON DUPLICATE KEY UPDATE会触发它,REPLACE也会——这点常被忽略
批量场景下,VALUES(col1)在UPDATE子句里可引用刚插入的值,比如UPDATE count = count + VALUES(count),这个语法只有批量插入才支持。
Threads_running和Innodb_row_lock_time_avg指标。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










