在处理大量数据入库的场景时,单条执行INSERT语句的方式会产生大量的网络交互和事务开销,导致写入效率低下。优化批量插入逻辑可以大幅缩短数据写入耗时,减少数据库的资源消耗。

常见SQL批量插入方法
1. 多值INSERT语句
这是最基础的批量插入方式,通过在一条INSERT语句中拼接多个值集合,一次性向表中插入多条数据,减少SQL语句的解析和网络传输次数。
以MySQL为例,语法格式如下:
-- 向用户表批量插入3条数据
INSERT INTO user (username, age, email)
VALUES
('张三', 25, 'zhangsan@ipipp.com'),
('李四', 28, 'lisi@ipipp.com'),
('王五', 22, 'wangwu@ipipp.com');
这种方式适合插入数据量较少的场景,通常建议单条语句的值集合数量控制在1000以内,避免SQL语句过长导致解析耗时增加。
2. 事务包裹批量插入
如果插入的数据量较大,多值INSERT的方式会导致单条SQL过长,此时可以将多条INSERT语句放在同一个事务中执行,减少事务提交的次数,提升写入效率。
示例代码如下:
-- 开启事务
START TRANSACTION;
-- 执行多条插入语句
INSERT INTO user (username, age, email) VALUES ('张三', 25, 'zhangsan@ipipp.com');
INSERT INTO user (username, age, email) VALUES ('李四', 28, 'lisi@ipipp.com');
INSERT INTO user (username, age, email) VALUES ('王五', 22, 'wangwu@ipipp.com');
-- 提交事务
COMMIT;
这种方式下,所有插入操作要么全部成功,要么全部回滚,同时减少了事务提交的网络开销,性能比单条无事务插入高很多。
3. LOAD DATA导入文件数据
当需要处理十万甚至百万级别的大批量数据时,使用LOAD DATA语句从本地文件直接导入数据是效率最高的方式,它绕过了SQL语句解析的过程,直接将数据加载到表中。
首先准备一个数据文件user_data.txt,内容以制表符分隔:
张三 25 zhangsan@ipipp.com 李四 28 lisi@ipipp.com 王五 22 wangwu@ipipp.com
执行导入语句:
-- 从本地文件导入数据到user表 LOAD DATA LOCAL INFILE '/tmp/user_data.txt' INTO TABLE user FIELDS TERMINATED BY 't' -- 字段分隔符为制表符 LINES TERMINATED BY 'n' -- 行分隔符为换行符 (username, age, email); -- 对应表的字段
这种方式是MySQL中批量导入数据的首选方案,导入速度比常规INSERT方式快数倍甚至数十倍。
不同方案性能对比
以下是三种常见方案在插入1万条数据时的性能对比(基于MySQL 8.0环境测试):
| 插入方案 | 耗时(毫秒) | 适用场景 |
|---|---|---|
| 单条无事务INSERT | 约12000 | 仅适合极少量数据插入 |
| 多值INSERT(每1000条一批) | 约800 | 数据量中等,1万-10万条 |
| 事务包裹INSERT(每1000条一批) | 约500 | 数据量中等,需要事务保证 |
| LOAD DATA文件导入 | 约120 | 大批量数据,10万条以上 |
批量插入注意事项
- 插入数据时需要避免唯一键冲突,可以在INSERT后添加
ON DUPLICATE KEY UPDATE或者IGNORE关键字处理冲突场景。 - 批量插入时如果表有自增主键,建议提前关闭自增列的校验,插入完成后再开启,减少开销。
- 生产环境执行大批量插入前,建议先在测试环境验证SQL的正确性,避免误写入错误数据。
- 如果使用的是其他数据库,比如PostgreSQL可以使用
COPY命令实现类似LOAD DATA的高效导入,SQL Server可以使用BULK INSERT语句。
编程语言中的批量插入实现
在实际开发中,通常会通过编程语言操作数据库实现批量插入,以下是Java使用JDBC实现批量插入的示例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
public class BatchInsertDemo {
public static void main(String[] args) throws Exception {
// 模拟待插入的用户数据
List<User> userList = new ArrayList<>();
userList.add(new User("张三", 25, "zhangsan@ipipp.com"));
userList.add(new User("李四", 28, "lisi@ipipp.com"));
userList.add(new User("王五", 22, "wangwu@ipipp.com"));
// 数据库连接信息
String url = "jdbc:mysql://127.0.0.1:3306/test_db?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "123456";
try (Connection conn = DriverManager.getConnection(url, username, password)) {
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
String sql = "INSERT INTO user (username, age, email) VALUES (?, ?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
// 遍历数据添加到批处理
for (User user : userList) {
ps.setString(1, user.getUsername());
ps.setInt(2, user.getAge());
ps.setString(3, user.getEmail());
ps.addBatch();
}
// 执行批处理
ps.executeBatch();
// 提交事务
conn.commit();
} catch (Exception e) {
// 异常回滚
conn.rollback();
throw e;
}
}
}
// 用户实体类
static class User {
private String username;
private int age;
private String email;
public User(String username, int age, String email) {
this.username = username;
this.age = age;
this.email = email;
}
public String getUsername() {
return username;
}
public int getAge() {
return age;
}
public String getEmail() {
return email;
}
}
}
上述代码通过JDBC的addBatch和executeBatch方法实现批量插入,同时配合事务控制提升写入效率,是Java开发中常用的批量写入实现方式。