mysql原生支持单条insert into语句插入多行数据,语法为values后跟多个括号元组并用逗号分隔;建议每批500–1000行,需配合事务、注意max_allowed_packet限制及null显式书写。

INSERT INTO ... VALUES 一次插多行最直接
MySQL原生支持在单条INSERT INTO语句中写多个VALUES元组,这是批量插入的首选方式,性能好、语法简洁、事务原子性有保障。
常见错误是把多条INSERT拼成一个字符串再执行(比如循环生成多条INSERT INTO t VALUES (...)),这不仅慢,还容易触发max_allowed_packet限制或SQL注入风险。
- 每行
VALUES用逗号分隔,括号不能省:INSERT INTO users (name, age) VALUES ('Alice', 25), ('Bob', 30), ('Charlie', 28); - 单次建议控制在 500–1000 行以内;超过 5000 行可能触发
max_allowed_packet(默认 4MB),需同步调大该参数 - 所有值必须类型匹配,NULL 要显式写
NULL,不能留空或用空字符串代替
用LOAD DATA INFILE导入大文件更高效
当数据量超过几万行,或者源是 CSV/TSV 文件时,LOAD DATA INFILE比INSERT快 5–10 倍,因为它绕过SQL解析层,直接读取文本并转换为行记录。
典型场景:ETL 导入日志表、初始化维度表、迁移旧系统导出的数据。
- 文件必须位于 MySQL 服务端本地(不是客户端),路径写绝对路径:
LOAD DATA INFILE '/var/lib/mysql-files/data.csv' INTO TABLE logs; - 需要
FILE权限,且secure_file_priv配置允许该路径(查SHOW VARIABLES LIKE 'secure_file_priv';) - 字段分隔符、换行符、是否跳过首行都可配,例如:
FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS
INSERT IGNORE / ON DUPLICATE KEY UPDATE 处理重复
批量插入时遇到主键或唯一索引冲突,默认会报错中断整个语句。用INSERT IGNORE或ON DUPLICATE KEY UPDATE可让插入“柔性失败”或自动合并。
注意:IGNORE会静默跳过冲突行,不报错但也不告诉你哪几行被跳了;ON DUPLICATE KEY UPDATE适合做“存在则更新计数器”这类逻辑。
INSERT IGNORE INTO stats (day, type, count) VALUES ('2024-06-01', 'login', 120), ('2024-06-01', 'logout', 95);INSERT INTO stats (day, type, count) VALUES ('2024-06-01', 'login', 10) ON DUPLICATE KEY UPDATE count = count + VALUES(count);- 只对定义在
UNIQUE或PRIMARY KEY上的列生效;普通索引不触发
用事务包住批量INSERT避免部分写入
即使用了多行VALUES,如果没加事务,每条INSERT仍可能被自动提交(取决于autocommit设置)。网络中断或进程崩溃会导致只插了一半数据。
尤其在应用层拼SQL批量提交时,这点极易被忽略——你以为是一次操作,其实MySQL可能按默认行为拆成多次提交。
- 显式开启:
START TRANSACTION;→ 批量INSERT→COMMIT; - 确认
autocommit状态:SELECT @@autocommit;,生产环境建议设为 0 - 不要在循环里每插一批就
COMMIT一次(比如每100行commit),这会放大I/O开销;合理批次大小+单事务更稳
真正要注意的是:批量插入不是“越大批越好”,而是要平衡max_allowed_packet、事务日志增长、锁持有时间三者。线上表如果有长事务或高并发更新,一次插 5000 行可能阻塞其他操作;而插 50 行又太碎。实测从 500 行起步调优,观察 slow log 和Innodb_row_lock_waits指标最靠谱。











