游客发表

mysql批量插入sql

发帖时间:2026-10-17 04:50:21

一次插入 10 万条数据,量插用逐条 INSERT 跑,量插等了两分半。量插换成批量插入,量插1.8 秒完事。量插差距就是量插这么离谱。

MySQL 的量插每条 SQL 都是一次网络往返,逐条插入意味着 10 万次客户端和服务端的量插握手。批量插入的量插核心逻辑很简单:把多条记录塞进一条 INSERT 语句里,让网络开销从 N 次降到 1 次。量插

基本写法

INSERT INTO users (name,量插 age, email) VALUES ('张三', 25, 'zhangsan@example.com'), ('李四', 30, 'lisi@example.com'), ('王五', 28, 'wangwu@example.com');

就这么直接。一条语句,量插三行数据。量插MySQL 会把它当作单次操作执行,量插省掉了重复的量插解析、连接、事务开销。

但这里有个坑很多人踩过:max_allowed_packet。MySQL 默认这个值是 4MB 或 64MB(取决于版本),你一次性拼出来的 SQL 字符串超过了这个限制,服务端直接拒绝,报 Packet too large。解决办法要么调大这个参数,要么在应用层分批。

批量插入到底快在哪

我拿一张简单的表做过对比测试:

CREATE TABLE test_batch ( id INT AUTO_INCREMENT PRIMARY KEY, val VARCHAR(64), num INT );

逐条插入 5 万条,耗时约 72 秒。每 1000 条一批批量插入,耗时约 1.2 秒。60 倍的差距。

原因不复杂。逐条插入时,每次都要走一遍完整的 SQL 解析流程、权限检查、查询缓存查找、事务日志写入。批量插入把这些开销摊薄到一批数据上,单位成本骤降。

还有个容易被忽略的点:如果你用的是 InnoDB 且没显式开事务,每条 INSERT 都会自动提交一次,每次提交都触发一次 redo log 刷盘。批量插入天然就是一条语句一个事务,刷盘次数少得多。

再快一步:事务 + 批量

批量插入本身已经很快了,但如果数据量特别大需要分批,记得手动包一个事务:

START TRANSACTION; INSERT INTO test_batch (val, num) VALUES (...), (...), ...; INSERT INTO test_batch (val, num) VALUES (...), (...), ...; COMMIT;

不包事务的话,每批 INSERT 都会独立提交,性能白白丢掉一截。包上事务之后,所有批次的写入共享一次提交,redo log 只在最终 COMMIT 时刷一次。

实测:5 万条数据分 50 批,不包事务约 3.5 秒,包事务约 1.8 秒。差了将近一倍。

MyBatis 里的批量插入

Java 项目用 MyBatis 的话,foreach 标签拼接是最常见的做法:

<insert id="batchInsert"> INSERT INTO users (name, age) VALUES <foreach collection="list" item="item" separator=","> (#{ item.name}, #{ item.age}) </foreach> </insert>

但要注意 JDBC 连接串得加上 rewriteBatchedStatements=true,否则 MySQL 驱动会把 foreach 拼出来的 SQL 又拆成一条条发送,批量效果直接归零。这个参数是 MySQL Connector/J 的隐藏开关,不加的话你以为在批量,实际上还是逐条。

什么时候不该用批量插入

批量不是万能药。数据量小(几十条以内),逐条插和批量插的差距肉眼看不出来,代码可读性反而更重要。另外,如果需要拿到每条插入的自增 ID,批量插入就比较麻烦——LAST_INSERT_ID() 只返回第一行的 ID,后续的得自己推算。这种情况要么逐条插,要么插完再查一次。

还有个实际经验:批量插入的 SQL 别拼太长。我一般控制在 500 到 1000 条一组,既能吃到批量的性能红利,又不会撑爆 max_allowed_packet,出错时回滚的代价也可控。一组 5000 条以上,一旦中间某条数据有问题,整个批次全部失败,排查起来很痛苦。

批量插入的本质就是用空间换时间——把 N 次网络交互压缩成 1 次。理解了这一点,剩下的都是参数调优的事。

    热门排行

    友情链接