用 LOAD DATA INFILE
替代 INSERT
语句
单条
INSERT插入万级数据时,网络往返、SQL 解析、事务开销会严重拖慢速度。而
LOAD DATA INFILE是 MySQL 原生批量加载机制,跳过大部分 SQL 层处理,直接解析文本写入引擎层,实测快 5–20 倍。
关键点:
文件必须位于 MySQL 服务端(不是你本地),路径是服务端视角的绝对路径,如/var/lib/mysql-files/data.csv需开启
secure_file_priv对应目录,查看命令:
SHOW VARIABLES LIKE 'secure_file_priv';字段分隔符、行结束符要与文件严格一致,例如 CSV 常用:
FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n'若表有主键/唯一索引,
LOAD DATA INFILE默认不跳过冲突,加
IGNORE或用
REPLACE控制行为
关闭自动提交 + 手动控制事务边界
默认每条
INSERT都是一次独立事务,频繁刷盘导致 I/O 瓶颈。即使不用
LOAD DATA INFILE,也应显式合并写入批次。
操作建议:
执行前设SET autocommit = 0;,插入完成后
COMMIT;每批控制在 1000–10000 行之间(取决于单行大小和内存),太小起不到合并效果,太大可能触发锁等待或回滚日志溢出 避免在事务中混杂
SELECT或其他 DML,尤其是长事务下容易阻塞 MVCC 清理 MyISAM 引擎不支持事务,此优化仅适用于 InnoDB
导入前临时调整表结构与配置
有些“保护性”设置在批量写入时反而成为性能枷锁,可阶段性关闭:
禁用非必要索引:ALTER TABLE t DROP INDEX idx_xxx;,导入完成再重建。注意:主键和外键约束不能删,但外键检查可临时关:
SET FOREIGN_KEY_CHECKS = 0;关掉唯一性校验(仅限确认数据无冲突时):
SET UNIQUE_CHECKS = 0;,否则每行都要查唯一索引 增大
innodb_log_file_size和
innodb_buffer_pool_size(需重启生效),但线上调优需谨慎评估内存压力 如果源数据已按主键排序,且表是 InnoDB,导入前
ORDER BY主键能显著减少页分裂
避免使用 ORM 或通用工具做大批量导入
像 Django 的
bulk_create()、Spring Data JPA 的
saveAll()或 phpMyAdmin 等界面工具,底层仍是拼接多条
INSERT或分批提交,封装层带来额外序列化、连接复用、结果集处理开销。
更高效的做法:
用原生客户端命令行直连:mysql -u user -p db_name (适合纯 SQL 文件)用
mysqlimport工具,本质是
LOAD DATA INFILE的封装,支持并发、压缩文件(
--compress)、字段映射等 Python 可用
pymysql或
mysql-connector-python的
executemany(),但务必配合
autocommit=False和合理 batch_size 注意字符集:确保文件编码(如 UTF8MB4)与连接、表定义一致,否则出现乱码或截断,错误信息类似
Incorrect string value: '\xF0\x9F\x98\x80'...
真正卡住效率的,往往不是磁盘或 CPU,而是事务粒度、索引维护和客户端协议开销。批量导入不是“越快越好”,而是“在可控范围内压榨存储引擎的吞吐能力”。临时关索引、调参数可以,但别忘了恢复;用
LOAD DATA INFILE要确认文件位置和权限——这两点漏掉,脚本跑十遍都报错。
