当前位置: 代码网 > it编程>数据库>Mysql > MySQL数据库批量插入数据的高效写法与优化技巧详解

MySQL数据库批量插入数据的高效写法与优化技巧详解

2026年09月23日 Mysql 我要评论
一、先说结论:最高效的写法长什么样无论你用哪种数据库,效率最高的批量插入,本质上都在做同一件事:把多次网络往返、多次事务提交、多次 sql 解析,合并成尽可能少的次数。以 mysql 为例,效率从高到

一、先说结论:最高效的写法长什么样

无论你用哪种数据库,效率最高的批量插入,本质上都在做同一件事把多次网络往返、多次事务提交、多次 sql 解析,合并成尽可能少的次数。

以 mysql 为例,效率从高到低大致是:

  • ✅ load data infile
  • ✅ 单条 insert 多 values(insert into t values (...),(...),(...))
  • ✅ 批量 + 手动事务(关闭 autocommit)
  • ✅ 单条 insert + autocommit(默认)
  • ❌ 循环里一条一条 insert

下面我们逐层拆解为什么,以及怎么写才对。

二、为什么“一行一行插”这么慢?

先看一段最常见的反面教材:

for (user user : userlist) {
    jdbctemplate.update(
        "insert into user(name, age) values(?, ?)",
        user.getname(), user.getage()
    );
}

表面看没毛病,但背后发生了什么?

每一次 insert 的隐藏成本

  1. 网络往返(rtt) ​:应用 → 数据库 → 返回结果,一次往返通常 0.5–2ms。
  2. sql 解析与执行计划​:每条 sql 都要解析、权限校验、生成执行计划。
  3. 事务日志刷盘(redo / binlog) ​:默认 autocommit=on,每插一行就刷一次日志。
  4. 索引维护​:每行插入都要更新聚簇索引 + 二级索引。

假设 1 万条数据,每行 1ms 纯插入成本,光网络往返就可能再吃掉 1–2 万次 rtt,整体从几百毫秒拖到几十秒。

三、第一层优化:单条 sql,多个 values

写法示例

insert into user (name, age)
values
  ('alice', 18),
  ('bob', 20),
  ('charlie', 22),
  -- ... 更多行
  ('zoe', 25);

为什么快?

  • 一次网络往返
  • 一次 sql 解析
  • 一次事务提交(或合并提交)
  • 索引批量维护,减少随机 io

实测对比(mysql 8.0,本地 ssd)

方式1 万行耗时
逐条 insert~12 秒
单 sql 多 values~0.3 秒

性能差距 30–40 倍。

注意事项

1. 单条 sql 别太大

mysql 有 max_allowed_packet(默认 64mb),sql 超大会报错。

推荐每批 500~2000 行,视单行字段大小而定:

list<user> batch;
for (int i = 0; i < users.size(); i += 1000) {
    batch = users.sublist(i, math.min(i + 1000, users.size()));
    insertbatch(batch);
}

2. 字段顺序要对齐

-- ❌ 容易出错
insert into user values ('alice', 18), (20, 'bob');

-- ✅ 显式指定列
insert into user (name, age) values (?, ?), (?, ?);

四、第二层优化:手动控制事务

即使你写了多 values,如果 autocommit=on,数据库仍可能每行/每批频繁刷日志。

正确姿势

start transaction;

insert into user (name, age) values (...),(...),...;

insert into user (name, age) values (...),(...),...;

commit;

效果

  • 多次插入共享一次事务
  • redo / binlog 只刷一次
  • 性能再提升 2–5 倍

jdbc 写法

conn.setautocommit(false);
preparedstatement ps = conn.preparestatement(
    "insert into user (name, age) values (?, ?)"
);
for (user u : users) {
    ps.setstring(1, u.getname());
    ps.setint(2, u.getage());
    ps.addbatch();
}
ps.executebatch();
conn.commit();

五、第三层优化:数据库专属“大杀器”

mysql:load data infile

这是 mysql 批量导入的天花板

load data infile '/data/users.csv'
into table user
fields terminated by ','
lines terminated by '\n'
(name, age);

为什么最快?

  • 跳过 sql 解析层
  • 直接按行解析、批量写页
  • 事务日志批量写入
  • 可以禁用索引后重建

性能对比

方式100 万行耗时
逐条 insert~20 分钟
多 values~30 秒
load data~3–5 秒

注意

  • 文件需在 mysql 服务器上(或用 local 走客户端)
  • 权限要求高
  • 不适合实时业务,适合初始化/迁移

postgresql:copy 命令

pg 的等价方案是 copy,性能同样碾压 insert。

copy user (name, age)
from '/data/users.csv'
delimiter ','
csv;

jdbc 用 copymanager api,性能比批量 insert 快 5–10 倍。

六、索引与表结构的隐藏陷阱

1. 插入前考虑“先删索引,再建回来”

如果你要导 百万级以上​ 数据:

alter table user disable keys;   -- myisam
-- 或手动记录索引,导入后重建
insert ...
create index ...

innodb 不能 disable keys,但可以:

  • 导入前不建二级索引
  • 导入完再 create index(比边插边维护快很多)

2. 自增主键 vs uuid

  • 自增 id:顺序写,页填充率高,插入快
  • uuid / 随机字符串:随机写,页分 裂频繁,插入慢 3–5 倍

批量导入时,主键顺序越连续,性能越好。

3. 关闭不必要的约束

大批量导入期间可临时关闭:

set foreign_key_checks = 0;   -- mysql
set unique_checks = 0;
-- 导入完再打开

七、不同语言的“正确姿势”

mybatis

<insert id="batchinsert">
    insert into user (name, age)
    values
    <foreach collection="list" item="u" separator=",">
        (#{u.name}, #{u.age})
    </foreach>
</insert>

配合:

rewritebatchedstatements=true   # mysql jdbc 参数

python(pymysql / sqlalchemy)

with engine.begin() as conn:
    conn.execute(
        user.__table__.insert(),
        [{"name": u.name, "age": u.age} for u in users]
    )

sqlalchemy executemany() 会自动批处理。

八、避坑清单

后果解法
循环里单条 insert慢 10–100 倍用多 values 或 batch
autocommit 开着频繁刷日志手动事务
单条 sql 太大超 max_allowed_packet分批 500–2000
表有太多索引插入慢导入前删索引
用 uuid 主键页分 裂改用自增或雪花 id
批量插还开触发器每行触发一次导入前 disable
网络延迟高rtt 放大合并批次 + 长连接

九、决策树:你该用哪种方式?

数据量 < 1 万?
  └─ 是 → 多 values + 手动事务 ✅

数据量 1 万 ~ 100 万?
  └─ 是 → 分批多 values + 手动事务 + 关索引 ✅

数据量 > 100 万?
  └─ 是 → load data / copy + 无索引导入 ✅

需要实时写入?
  └─ 是 → 消息队列攒批 → 定时刷库 ✅

十、总结

批量插入的最高境界,不是“写一条更快的 sql”,而是“尽量少写 sql”。

记住三句话:

  1. 能一次插 1000 行,就别插 1000 次 1 行
  2. 能一次提交,就别提交 1000 次
  3. 能用 load data / copy,就别用 insert

把网络往返、sql 解析、事务刷盘这三座大山削平,批量插入就能从“分钟级”变成“秒级”。

以上就是mysql数据库批量插入数据的高效写法与优化技巧详解的详细内容,更多关于mysql批量插入数据的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com