Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql批量插入数据最快方法

mysql批量插入数据最快方法是什么?这4种方案哪个更高效?

作者:legendC_

还在为MySQL插入大量数据慢而头疼吗?本文深度对比了4种主流批量插入方案:单条多值INSERT、JDBC批处理、MyBatis批处理和LOAD DATA INFILE,从性能、资源消耗、实现复杂度等维度帮你选出最适合业务场景的批量插入方法

背景

批量插入指的是通过单次数据库交互操作向 MySQL 服务器一次性插入多条数据记录的技术。与逐条插入相比,它能显著提升大数据量写入时的效率,其必要性主要体现在以下三个核心方面:

1.性能提升​(指的是响应时间)

在默认的逐条插入模式下,每条 INSERT语句都会带来完整的网络往返、SQL 解析、事务提交等开销。

当需要插入成千上万条数据时,这些开销会迅速累积,成为主要的性能瓶颈。

通过批量插入,可以将数百甚至数千条记录合并为一次操作,极大减少了这些重复性开销。

实验数据表明,批量插入的性能可以是逐条插入的数十倍甚至上百倍。

例如,插入 10,000 条数据时,逐条插入可能需要分钟级别的时间,而一个合适的批量插入方案可以在秒级甚至毫秒级完成。

2.​资源优化(指的是服务端资源)

3.​应对特定场景需求​

在许多实际业务场景中,都存在需要快速写入大量数据的需求。

例如:

批量任务处理​(多种场景的本质上都是批处理):如电商平台的每日订单归档等

性能指标

逐条插入

批量插入

优势说明

网络往返次数

高 (N次)

极低 (N/1000次量级)

合并数据,大幅减少网络延迟影响

SQL解析开销

高 (N次)

低 (1次或很少几次)

数据库只需解析少量SQL语句

事务开销

高 (默认N次提交)

低 (1次或很少几次提交)

减少日志刷盘次数,降低I/O压力,特别是redo log记录批次操作,IO一次写入,效率较高

锁持有时间

减少锁竞争,在执行过程中一直持有写锁,平滑锁的获取和释放过程,提高数据库并发性

总体耗时

线性增长 (O(N))

亚线性增长 (O(log N))

数据量越大,性能优势越明显

方案选型

方案一:单条 INSERT 语句插入多行值

这是最基础、最直接的批量插入方法,它通过一条 INSERT INTO ... VALUES ..., ..., ...语句一次性插入多条记录。

底层原理:

不足:

// Java代码中拼接
StringBuilder sql = new StringBuilder("INSERT INTO users (name, email, age) VALUES ");
for (int i = 0; i < userList.size(); i++) {
    User user = userList.get(i);
    sql.append("('").append(user.getName()).append("', '")
       .append(user.getEmail()).append("', ").append(user.getAge()).append(")");
    if (i != userList.size() - 1) {
        sql.append(", ");
    }
}
Statement.execute(sql.toString())
 // 使用Mybatis时使用for-each标签拼接
  <insert id="batchInsert">
    INSERT INTO users (name, email, age) VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.name}, #{user.email}, #{user.age})
    </foreach>
</insert>

方案二:JDBC批处理

底层原理

Without rewriteBatchedStatements​:JDBC 驱动会简单地发送多条预处理语句,效果类似于逐条执行,性能提升有限。

With rewriteBatchedStatements=true​:这是性能关键。MySQL JDBC 驱动会进行“魔法”重写:

不足:

String sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);

conn.setAutoCommit(false); // 关闭自动提交,开启事务

for (User user : userList) {
    pstmt.setString(1, user.getName());
    pstmt.setString(2, user.getEmail());
    pstmt.setInt(3, user.getAge());
    pstmt.addBatch(); // 将一组参数加入批处理缓存
    
    // 每积累一定数量执行一次,防止缓存过大
    if (i % 1000 == 0) {
        pstmt.executeBatch();
        pstmt.clearBatch();
    }
}
pstmt.executeBatch(); // 不要忘了flush最后的缓存
conn.commit(); // 手动提交

pstmt.close();

方案三:MyBatis批处理

底层本质上依赖于jdbc批处理,只是在代码正常执行SQL时,SQL被MyBatis缓存起来,在事务提交时执行jdbc的executeBatch。

Mybatis只是对操作的包装,其代码实现更为简洁,而且性能接近于jdbc批处理,最为常用。

但是和jdbc使用时一样,其会在内存中sqlSession缓存所有对象和sql(本质上是已设置参数的 PreparedStatement对象),需要关注于内存开销

方案四:直接加载文件

切记,加载文件本身并不会暂停数据库的访问,但是会在对应的表上加锁(通常是表级的写锁),而且加载文件对MYSQL服务端来说就是一条命令,其一样要通过编译、语法解析等操作,并防在事务中执行。

底层原理

LOAD DATA INFILE最快,是因为它几乎绕过了 MySQL 的 SQL 处理层

直接解析文件​:MySQL 使用一个高度优化的专用程序来读取和解析数据文件,速度远快于解析 SQL 语句。

最小化日志记录​:该命令可以以最小化的方式记录日志,大大减少了 I/O 操作。

索引更新优化​:

事务处理​:整个加载过程通常被视为一个事务,只需一次提交。

优点

缺点

"LOAD DATA INFILE '/tmp/users.csv' " +
                 "INTO TABLE users " +
                 "FIELDS TERMINATED BY ',' " +
                 "LINES TERMINATED BY '\\n' " +
                 "(name, email, age)"

评估维度

单条多值INSERT

JDBC 批处理

MyBatis批处理

LOAD DATA INFILE

实现复杂度

插入性能

理论上限极高

非常高​ 

高 (依赖JDBC,内部需要遍历preparedStatement,有一定开销)

极致

网络开销

极低

极低

极低

极低 (仅传命令)

内存消耗

应用端高 (拼接SQL)

应用端高​ (Session缓存)

低 (数据库端处理)

错误处理

整批失败

可灵活分批次

整批失败风险

文件格式错误

业务适应性

通用

通用

MyBatis项目专用

离线数据导入

安全性

需防SQL注入

安全 (预编译)​

安全 (预编译)​

需控制文件权限

总结

在选择合适的 MySQL 批量插入方案时,需要从性能、可靠性、实现复杂度和具体业务场景等多个维度进行综合考量。每种方案有各自适合的场景,需要综合评估。

通用优化策略​:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文