批量INSERT比单条快,因其仅1次SQL解析、事务提交、日志刷盘和锁操作,大幅减少网络RTT、CPU开销及索引分裂;但受max_allowed_packet限制,建议每批1000–5000行并配事务优化。

批量 INSERT 比循环单条插入快,不是因为“数据库更喜欢大SQL”,而是单条插入在默认配置下触发了大量隐式开销——事务提交、日志刷盘、语句解析、锁竞争全被放大了 N 倍。
每次单条INSERT都默认提交一次事务
MySQL 默认 autocommit=1,每执行一条 INSERT,就等价于:
BEGIN; INSERT INTO t VALUES (...); COMMIT;
这意味着:每条记录都要走一遍 redo log 写入、fsync 刷盘、锁获取与释放、事务状态切换。10 万条 = 10 万次磁盘 I/O(或至少 10 万次日志缓冲区同步)。而批量写入通常包裹在显式事务中,只刷盘 1 次。
- 实测中,关闭自动提交后用
BEGIN; ... 1000 条 INSERT ... COMMIT;,耗时可从 42 秒降到 1.7 秒 - PostgreSQL 的 WAL 写入压力、SQL Server 的 LDF 日志增长,同样遵循这个规律
- 别依赖 ORM 的“批量”标识——如 MyBatis 的
<foreach></foreach>若没配batch=true和事务,仍是 N 次独立提交
单条INSERT重复解析、编译、权限检查
循环调用 INSERT INTO t VALUES (?) 时,即使参数化,数据库仍需对每条语句做:
- 语法解析(哪怕结构相同)
- 语义校验(列是否存在、类型是否可隐式转换)
- 权限检查(INSERT 权限、表级/列级)
- 执行计划生成(尤其当统计信息变动时可能重编译)
而一条多值 INSERT INTO t VALUES (a),(b),(c) 只解析 1 次,执行计划复用,权限检查也只做 1 次。JDBC 中启用 rewriteBatchedStatements=true 就是让驱动把 N 条单值语句“重写”成 1 条多值语句,本质就是绕过重复解析。
网络与协议层开销被线性放大
客户端到数据库的每次 SQL 请求,都包含 TCP 包头、MySQL 协议包封装、响应等待。单条插入下:
- 10 万次 round-trip(RTT),哪怕平均 0.5ms,光网络延迟就占 50 秒
- 每条语句还要传输额外元数据(如字符集、连接 ID、状态标志)
- 服务端连接线程要反复调度、上下文切换,高并发下易成为瓶颈
批量插入把 1000 行压进 1 个协议包(只要不超过 max_allowed_packet),网络往返次数从 N 降到 N/1000 级别。这也是为什么 LOAD DATA INFILE 更快——它直接跳过 SQL 协议层,走服务端文件读取路径。
容易被忽略的约束和索引实时开销
每插入一行,InnoDB 都要:
- 检查所有唯一索引是否冲突(逐行哈希或 B+ 树查找)
- 更新二级索引叶子节点(每索引 1 次随机写)
- 触发外键约束校验(关联表查主键)
- 执行
BEFORE/AFTER INSERT触发器(若存在)
这些操作在单条模式下无法合并或批处理,只能串行执行。而批量导入前临时禁用 SET unique_checks=0 和 SET foreign_key_checks=0,能让导入速度提升 3–8 倍——但必须确保数据本身无冲突,且导入后立即恢复并验证。


















