大批量INSERT慢的关键在于事务开销和索引维护;应使用显式事务合并多行插入、禁用非必要约束、优先选用LOAD DATA INFILE,并分批限流执行。

大批量 INSERT 为什么慢?关键在事务和索引维护
MySQL 默认每条 INSERT 都是独立语句,意味着每次都要走完整事务流程(日志写入、锁竞争、索引 B+ 树分裂),数据量一过万行,性能断崖式下跌。更隐蔽的问题是:即使关了自动提交,如果没控制好 innodb_buffer_pool_size 或频繁触发刷脏页,吞吐照样上不去。
实操建议:
- 务必用显式事务包裹多条
INSERT,避免每条都开事务:START TRANSACTION; INSERT INTO t VALUES (1,'a'),(2,'b'),(3,'c'); INSERT INTO t VALUES (4,'d'),(5,'e'); COMMIT;
- 单次
INSERT尽量合并多行(最多 1000 行左右),太多会触发max_allowed_packet限制或内存溢出 - 插入前临时关闭唯一性检查和外键约束(仅限可信数据):
SET unique_checks=0; SET foreign_key_checks=0;,插完再开
LOAD DATA INFILE 比 INSERT 快 5–10 倍,但要注意权限和路径
LOAD DATA INFILE 是 MySQL 原生批量导入机制,绕过 SQL 解析层,直接解析文本写入引擎,适合从 CSV/TSV 文件导入百万级以上数据。但它不是万能的——文件必须位于数据库服务器本地(除非用 LOCAL 关键字,但需客户端和服务端都开启 local_infile)。
常见错误现象:ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option,说明服务端限制了可读目录。
实操建议:
- 确认服务端配置:
SHOW VARIABLES LIKE 'secure_file_priv';,把文件放进去指定目录(如/var/lib/mysql-files/) - 用
FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n'精确匹配你的文件格式 - 插入前禁用索引:
ALTER TABLE t DISABLE KEYS;,导入完成再ENABLE KEYS;(仅对 MyISAM 有效;InnoDB 请改用DROP INDEX+ADD INDEX)
INSERT IGNORE / ON DUPLICATE KEY UPDATE 不是“批量优化”,而是冲突处理策略
很多人误以为加了 IGNORE 或 ON DUPLICATE KEY UPDATE 就能加速批量插入,其实它们只影响冲突行为,不改变单条语句执行路径。反而因额外的唯一键查找和更新逻辑,可能比普通 INSERT 更慢。
使用场景很明确:你确实需要去重或更新已存在记录,而不是单纯提速。
实操建议:
- 如果只是避免重复报错,且主键/唯一键冲突率低,
INSERT IGNORE可读性好 - 若需更新部分字段,优先用
ON DUPLICATE KEY UPDATE col=VALUES(col),注意VALUES()函数取的是本次 INSERT 的值,不是原值 - 千万避免在高并发下用
ON DUPLICATE KEY UPDATE更新计数器类字段(如cnt = cnt + 1),容易引发间隙锁争用
分批次 + 限流才是生产环境最稳的方案
一次性塞 100 万行,失败就得全滚回;网络抖动、锁超时、内存不足都可能导致中断。真实业务中,更可靠的做法是拆成 5000–10000 行/批,每批独立事务,并加简单限流(比如每秒最多 2 批)。
容易被忽略的点:
- 不要依赖客户端 sleep 控制节奏,应由服务端控制(例如用存储过程或应用层带 backoff 的重试)
- 每批执行后检查
ROW_COUNT(),确认实际插入行数是否符合预期(防止因主键冲突静默丢数据) - 大表插入期间,监控
Innodb_row_lock_waits和Created_tmp_disk_tables,异常飙升说明缓冲区或索引设计有问题
SHOW ENGINE INNODB STATUS 和慢日志才能定位。










