大批量INSERT慢的主因是autocommit=1导致每行独立事务,叠加二级索引维护、唯一键检查等开销;应关闭autocommit、用显式事务分批(500–2000行)、优先LOAD DATA INFILE,并控制事务粒度匹配监控与回滚能力。

大批量 INSERT 为什么慢?不只是“数据多”那么简单
单条 INSERT 走一次磁盘写、一次日志刷盘、一次事务提交,10 万行就是 10 万次开销。InnoDB 默认每条语句自动开启并提交事务,autocommit=1 是罪魁之一;同时,逐条插入还频繁触发二级索引维护、唯一键检查、缓冲池页分裂,这些在批量场景下被严重放大。
- 别只盯着 SQL 写法——先关掉
autocommit,用显式事务包住所有插入 - 确保表有主键(无主键时 InnoDB 会建隐藏聚簇索引,批量写入更易争用)
- 避免在插入过程中同时做
SELECT ... FOR UPDATE或长事务读,会加剧锁等待
用 INSERT INTO ... VALUES (...), (...), ... 一次塞多行
这是最简单有效的批量写法,但有明确边界:单条语句不能超过 max_allowed_packet(默认 4MB),且行数过多会导致解析耗时上升、锁持有时间变长、失败后回滚代价高。
- 推荐每批 500–2000 行,具体看单行平均长度;例如平均 200 字节,2000 行 ≈ 400KB,安全冗余充足
- 不要拼接超长字符串再执行——容易触发 OOM 或被 MySQL 中断,应在应用层分批构造
INSERT语句 - 如果字段含
NULL、特殊字符或二进制内容,务必用参数化方式(如 PDO 的prepare()+execute()),别手动拼 SQL
用 LOAD DATA INFILE 替代 INSERT(本地文件导入场景)
当数据已存在本地文件(如 CSV),LOAD DATA INFILE 比等量 INSERT 快 5–20 倍,因为它绕过 SQL 解析层,直接走存储引擎接口,且支持并行解析和批量刷脏页。
- 必须确保 MySQL 服务端能读取该文件路径(不是客户端机器);若用
LOAD DATA LOCAL INFILE,需服务端开启local_infile=ON且客户端驱动允许 - 字段分隔符、行结束符要和文件严格一致,否则会静默跳过错误行;建议先导出一小段测试,再查
SHOW WARNINGS - 导入前临时关闭唯一键检查:
SET unique_checks=0,导入后再开;但要注意这会跳过重复校验,业务上需自行兜底
事务大小怎么控制?别卡在“全成功 or 全失败”思维里
大事务看似原子性强,实则风险集中:锁范围大、undo 日志暴涨、主从延迟陡增、崩溃恢复时间不可控。真正健壮的做法是“分段提交”,而非“一气呵成”。
- 按主键/时间范围分片(比如按
created_at每小时一批),比按行数更利于后续定位问题 - 每次事务提交后,检查
SELECT ROW_COUNT()是否符合预期,避免因部分行被过滤(如INSERT IGNORE)导致数据丢失却不报警 - 别在事务中混用 DDL(如
ALTER TABLE)——它会隐式提交当前事务,导致你以为的“一批”实际被切开
批量插入真正的复杂点不在语法,而在如何让事务粒度匹配你的监控能力、回滚成本和上下游一致性要求。行数只是表象,锁行为、日志增长、主从复制位点偏移才是需要盯着看的实际指标。











