能,mysql原生支持单条insert语句插入多条记录,语法为insert into t (a,b) values (1,2), (3,4), (5,6);错误写法是用分号分隔多条独立insert,多数驱动默认禁用。

MySQL INSERT 一次能插入多条记录吗
能,而且官方支持。单条 INSERT 语句配合多个 VALUES 子句,就是最基础、最兼容的批量插入方式。
它不是“模拟”批量,而是 MySQL 原生语法,5.7 和 8.0 都完全支持,也不依赖客户端驱动特殊处理。
常见错误是写成多个独立的 INSERT 语句用分号连在一起,比如:
INSERT INTO t (a,b) VALUES (1,2); INSERT INTO t (a,b) VALUES (3,4);
这在命令行或某些客户端里看似能执行,但本质是**多条语句**,多数编程语言的 MySQL 驱动(如 Python 的 pymysql、Go 的 database/sql)默认禁用多语句执行,会直接报错 Commands out of sync 或 multi-statement disabled。
正确写法是合并为一条:
INSERT INTO t (a,b) VALUES (1,2), (3,4), (5,6);
INSERT … VALUES 和 INSERT … select 哪个更适合批量导入
取决于数据来源:
- 如果数据已在应用内存中(比如从 CSV 解析出几千行),用
INSERT ... VALUES拼接多值是最直接的选择; - 如果目标数据已在另一张表、或可通过 SQL 计算得出(比如归档老数据、按条件聚合后落库),
INSERT ... SELECT更安全高效,避免网络传输和拼 SQL 的风险; -
INSERT ... SELECT还能天然绕过应用层的字符编码、NULL 处理等陷阱,但无法插入常量混合字段(除非用SELECT 1 AS a, 'foo' AS b这种写法)。
注意:INSERT ... SELECT 在事务中可能触发更长的锁等待,尤其目标表有主键/唯一索引时,需留意死锁日志。
大批量插入时为什么加事务反而变慢了
不是事务本身慢,而是没控制好提交粒度。很多人写成:
BEGIN; INSERT INTO t VALUES (1,2), (3,4), ..., (99999,100000); COMMIT;
单事务塞几十万行,会导致:
- InnoDB redo log 写满被迫刷盘,频繁 fsync;
- undo log 膨胀,影响 MVCC 性能;
- 锁持有时间过长,阻塞其他读写;
- 一旦失败,整个事务回滚代价极高。
实操建议:每 1000–5000 行包一个事务,具体看单行大小和服务器 I/O 能力。例如 Python 中可循环分批:
for i in range(0, len(rows), 2000):<br> batch = rows[i:i+2000]<br> cursor.executemany("INSERT INTO t (a,b) VALUES (%s,%s)", batch)<br> conn.commit()
别用 executemany 直接喂十万条——它底层仍是逐条发 INSERT,不等价于多值 VALUES 语法。
LOAD DATA INFILE 和 INSERT … VALUES 的性能差距有多大
在千行级以上,LOAD DATA INFILE 通常快 5–20 倍,因为它绕过了 SQL 解析、权限检查、字符集转换等步骤,直接走存储引擎接口。
但它有硬性限制:
- 文件必须位于 MySQL 服务端本地(或启用了
local_infile=ON并信任客户端路径); - 需要
FILE权限,生产环境常被禁用; - 对字段类型校验弱,容易静默截断或转成 0,出错难定位;
- 不能触发
INSERT触发器,也不走外键约束检查(除非显式开启FOREIGN_KEY_CHECKS=1)。
所以日常开发用 INSERT ... VALUES 分批 + 事务更稳妥;etl 场景且可控环境下,优先上 LOAD DATA INFILE。
真正容易被忽略的是 autocommit 和 unique_checks 的组合影响:大批量插入前设 SET autocommit=0, unique_checks=0, foreign_key_checks=0 能再提一截速度,但结束后必须手动恢复,否则后续 DML 可能破坏数据一致性。