LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

数据库插入 1000 万数据?别再傻傻用 for 循环了!实测 5 种方式效率对比

zhenglin
2026年5月6日 9:4 本文热度 39

在日常的后端开发中,我们经常会遇到数据迁移、初始化、或者日志归档等场景,需要向数据库中导入海量数据。

"老板让我往数据库插 1000 万条数据,我写了个 for 循环,跑了一晚上还没跑完..."

如果你还在用 for 循环单条插入,那这篇通过实测数据说话的文章,绝对能帮你打开新世界的大门。今天我们就以 MySQL 为例,实测对比 5 种 常见的插入方式,看看谁才是真正的“性能之王”。

🛠️ 测试环境与准备

为了保证测试的公平性,我们统一测试环境:

  • 数据库:MySQL 8.0 (Docker 部署)

  • ORM 框架:Spring Data JPA (Hibernate) / MyBatis / JDBC

  • 测试数据量:1000 万条 (分批次测试)

  • 表结构:一张简单的用户表 user (id, username, password, email, create_time)

CREATE TABLE `user` (

  `id` bigint(20) NOT NULL AUTO_INCREMENT,

  `username` varchar(255) DEFAULT NULL,

  `password` varchar(255) DEFAULT NULL,

  `email` varchar(255) DEFAULT NULL,

  `create_time` datetime DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

1. 🐢 青铜选手:For 循环单条 Insert

这是最直观、最容易想到的方式,也是性能最差的方式。

代码示例 (JPA):

public void insertOneByOne(List<User> users) {

    for (User user : users) {

        userRepository.save(user);

    }

}

原理分析
每一次 save 操作,都会建立一次数据库连接,发送 SQL,执行,提交事务,关闭连接。
1000 万次网络 I/O + 1000 万次事务开销 = 灾难

实测结果
插入 1 万条数据耗时约 50 秒
推算插入 1000 万条数据需要 138 小时 (约 5.7 天)。
评价:除非你是在写 Hello World,否则严禁在生产环境使用。


2. 🥈 白银选手:JPA 的 saveAll (伪批量)

Spring Data JPA 提供了 saveAll 方法,看起来像是批量操作,但真的快吗?

代码示例:

public void saveAll(List<User> users) {

    userRepository.saveAll(users);

}

原理分析
默认配置下,Hibernate 的 saveAll 其实还是循环调用 save。虽然它在一个事务中执行,减少了事务提交的次数,但 SQL 依然是一条一条发的。
INSERT INTO user ...
INSERT INTO user ...

实测结果
插入 10 万条数据耗时约 12 秒
推算 1000 万条数据需要 20 分钟
评价:比单条快了不少,但依然不够看。

💡 优化 Tip
可以通过配置 spring.jpa.properties.hibernate.jdbc.batch_size=1000 开启 Hibernate 的批量插入支持,性能会有所提升,但依然受限于 Hibernate 的一级缓存机制,内存占用较高。


3. 🥇 黄金选手:MyBatis 的 foreach 拼接 SQL

这是 MyBatis 用户最常用的批量插入方式。

代码示例 (XML):

<insert id="batchInsert">

  INSERT INTO user (username, password, email, create_time) VALUES

  <foreach collection="list" item="item" separator=",">

    (#{item.username}, #{item.password}, #{item.email}, #{item.createTime})

  </foreach>

</insert>

原理分析
这种方式会生成一条巨长的 SQL:
INSERT INTO user (...) VALUES (...), (...), (...);
数据库只需要解析一次 SQL,构建一次执行计划,大大减少了网络 I/O 和数据库解析开销。

实测结果
插入 10 万条数据耗时约 2-3 秒
推算 1000 万条数据需要 3-5 分钟
评价:性能非常不错,是日常开发的首选。

⚠️ 注意

  • SQL 长度限制:MySQL 对 SQL 语句长度有限制 (max_allowed_packet),默认 4MB。如果一次拼接太多数据,会报错。建议分批,每批 1000-5000 条。


  • 解析成本:MyBatis 解析动态 SQL 也需要时间,数据量过大时解析会变慢。


4. 💎 钻石选手:原生 JDBC Batch

回归本质,使用最底层的 JDBC 批处理。

代码示例:

public void jdbcBatchInsert(List<User> users) {

    String sql = "INSERT INTO user (username, password, email, create_time) VALUES (?, ?, ?, ?)";

    try (Connection conn = dataSource.getConnection();

         PreparedStatement ps = conn.prepareStatement(sql)) {

        conn.setAutoCommit(false); // 开启事务

        for (int i = 0; i < users.size(); i++) {

            User user = users.get(i);

            ps.setString(1, user.getUsername());

            // ... 设置其他参数

            ps.addBatch();

            if ((i + 1) % 1000 == 0) {

                ps.executeBatch(); // 执行批处理

                ps.clearBatch();

            }

        }

        ps.executeBatch(); // 处理剩余数据

        conn.commit();

    } catch (Exception e) {

        e.printStackTrace();

    }

}


关键配置
连接字符串必须加上 rewriteBatchedStatements=true,否则 executeBatch 依然是一条条发送!
jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true

原理分析
开启 rewriteBatchedStatements 后,MySQL 驱动会在客户端将多条 INSERT 语句重写为 INSERT ... VALUES (...), (...) 的形式。相比 MyBatis,它省去了框架解析 XML 和映射对象的开销。

实测结果
插入 10 万条数据耗时约 1.5 秒
推算 1000 万条数据需要 2.5 分钟
评价:性能极致,内存占用低,适合对性能有极高要求的场景。


5. 👑 王者选手:MySQL LOAD DATA INFILE

如果说前面的都是在“写代码”,那这个就是在“开挂”。这是 MySQL 官方提供的文件导入命令。

代码示例:

LOAD DATA INFILE '/data/users.csv'

INTO TABLE user

FIELDS TERMINATED BY ',' 

LINES TERMINATED BY '\n'

(username, password, email, create_time);

原理分析
直接读取文件流,绕过了 SQL 解析层,直接操作存储引擎。这是数据库导入数据的最快方式,没有之一。

实测结果
插入 1000 万条数据耗时约 1-2 分钟 (取决于磁盘 IO)。
评价:降维打击。

缺点

  • 需要先生成文件(CSV/TXT)。


  • 需要数据库服务器的文件读取权限。


  • 逻辑较死板,不适合复杂的业务校验。


📊 最终排行榜 (1000 万数据估算)

 

总结与建议

  1. 日常开发 (几千/几万条) :直接用 MyBatis foreach,简单方便,性能足够。记得分批(每批 1000 条左右)。

  2. 高性能要求 (几十万/百万条) :使用 JDBC Batch,并开启 rewriteBatchedStatements=true。

  3. 海量数据迁移 (千万/亿级) :别犹豫,生成 CSV 文件,用 LOAD DATA INFILE

  4. 永远不要在循环里写 SQL 插入!


希望这篇文章能帮你避开性能坑,成为团队里的“性能优化大师”!


参考文章:原文链接


该文章在 2026/5/6 9:48:32 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-9  粤公网安备44030602007207号