load data infile比insert快得多,因其绕过sql解析、权限校验和网络往返,直接由存储引擎读取文件写入数据页;实测10万行csv导入仅需0.7秒,而单条insert耗时近90秒。

LOAD DATA INFILE 为什么比 INSERT 快得多
因为 LOAD DATA INFILE 绕过了 SQL 解析、权限校验(服务端模式)、客户端-服务端多次往返等开销,直接由存储引擎读取文件并写入数据页。实测 10 万行 CSV 导入,LOAD DATA INFILE 耗时约 0.7 秒,而单条 INSERT 需近 90 秒。
关键限制在于:服务端模式要求文件在 MySQL 服务器本地,且路径必须在 secure_file_priv 指定目录内;客户端模式(LOAD DATA LOCAL INFILE)需显式启用:set global local_infile = 1,且连接时加 --local-infile 参数。
常见错误现象:
-
ERROR 1148: The used command is not allowed with this MySQL version→ 未开启local_infile或客户端不支持 -
ERROR 29: File '/path/data.csv' not found→ 服务端模式下路径不在secure_file_priv目录,或权限不足(MySQL 进程用户不可读) - 中文乱码 → 必须显式指定
character set utf8mb4,不能依赖连接默认编码
多值 INSERT 的实际分批策略
把 10 万条数据拼成一条 INSERT INTO t VALUES (),(),...() 看似省事,但极易触发 max_allowed_packet 限制(默认 4MB),导致报错 Packets larger than max_allowed_packet are not allowed。
安全做法是按行数或字节长度主动分批,推荐每批 1000 行左右(具体看单行平均长度):
- MyBatis-Plus 的
saveBatch(list, batchSize)默认batchSize=1000,可直接用 - 原生 JDBC 使用
PreparedStatement.addBatch()+executeBatch(),也应控制每次addBatch()的调用次数 - 手写拼接时,建议用
StringBuilder构造语句,避免字符串频繁拷贝;每批末尾加;即可,无需拆成多条独立语句
注意:多值 INSERT 仍受事务隔离级别影响——若中途失败,整批回滚,无法部分成功。
INSERT IGNORE 和 ON DUPLICATE KEY UPDATE 的适用边界
当目标表有主键或唯一索引,且需跳过/更新重复记录时,这两个语法比先 SELECT 再 INSERT/UPDATE 快得多,因为只走一次索引查找。
区别很实际:
-
INSERT IGNORE INTO t (...) VALUES (...)→ 发现冲突直接丢弃整行,不报错也不更新,适合“只增不改”场景(如日志去重) -
INSERT INTO t (...) VALUES (...) ON DUPLICATE KEY UPDATE col = VALUES(col)→ 冲突时执行更新,VALUES(col)引用本次插入值,不是当前行旧值;适合“存在即更新”(如统计计数器、最后登录时间)
容易踩的坑:
- 仅对主键(
PRIMARY KEY)或唯一约束(UNIQUE KEY)生效,普通索引无效 -
ON DUPLICATE KEY UPDATE中若更新字段含自增列(如id),会导致警告甚至报错,应避免 - 使用
INSERT ... SELECT时,ON DUPLICATE KEY UPDATE仍可用,但别名引用要小心(VALUES(col)在子查询中不可用,需改用别名)
什么时候该用 INSERT ... SELECT 而不是 LOAD DATA
当你需要从已有表加工数据再插入(比如加条件过滤、字段转换、关联补全),INSERT INTO t1 SELECT ... FROM t2 WHERE ... 是最优解——全程在服务端内存完成,不经过网络、不生成中间文件、不触发 binlog 多次写入(取决于 binlog_format)。
典型场景:
- 跨库迁移:新老表结构不同,需字段映射或类型转换
- ETL 清洗:如
SELECT name, TRIM(email), DATE(created_at) FROM raw_users WHERE email REGEXP '^.+@.+\..+$' - 增量同步:
WHERE updated_at > (SELECT MAX(updated_at) FROM target_table)
性能关键点:
- 确保
SELECT部分能走索引,用EXPLAIN确认;否则全表扫描 + 临时表会拖慢整体 - 大表操作前加
SET SESSION sort_buffer_size = 4*1024*1024等调优参数(视服务器内存而定) - 如果目标表有大量二级索引,考虑先
DROP INDEX,导入完成后再重建,可提速 2–5 倍
真正难处理的,是既要格式转换、又要容错(比如某几行脏数据不能中断整个流程)、还要限速控资源——这时候就得回到 LOAD DATA + 前置脚本清洗,或者用专用工具如 mydumper/myloader。











