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