本文目录导读:

- 核心优化方案:MyBatis Batch Executor(推荐)
- 极致性能优化:MyBatis-Plus 或 原生Foreach(慎用)
- 底层优化手段
- 完整优化策略代码示例
- 性能对比(100万条数据插入MySQL)
- 关键优化总结
针对MyBatis的批量插入优化,核心思路是减少数据库连接交互次数、利用数据库原生批量能力以及合理控制事务与内存,以下是经过验证的几种优化方案,按推荐优先级排列:
核心优化方案:MyBatis Batch Executor(推荐)
这是MyBatis内置的批量模式,通过SqlSession的BATCH执行器实现。
代码示例:
// 获取批量模式的SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (User user : userList) {
mapper.insert(user); // 不会立即执行,而是批量缓存
}
sqlSession.commit(); // 一次性批量提交
} finally {
sqlSession.close();
}
优势:
- 底层将多次
INSERT语句合并为PreparedStatement的批量执行 - 相比普通循环插入,性能提升5-10倍
- 不会因单条SQL过长导致数据库报错
注意:
- 必须使用
BATCH模式,默认是SIMPLE(每次执行立即提交) - 每500-1000条提交一次,避免事务过大占用内存
- 不适用于
INSERT ... ON DUPLICATE KEY UPDATE等复杂语句(需配合foreach)
极致性能优化:MyBatis-Plus 或 原生Foreach(慎用)
单条INSERT拼接成多条:
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO user (name, age) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.name}, #{item.age})
</foreach>
</insert>
性能表现:
- 一次网络往返,数据库解析一次SQL
- 数据量较小时(<1000条)性能最佳
- 数据量过大(>5000条)可能导致数据库SQL过长、内存溢出
优化技巧:
// 手动分批处理,防止SQL过长
List<List<User>> partition = Lists.partition(userList, 1000);
for (List<User> batch : partition) {
mapper.batchInsert(batch); // 每1000条执行一次
}
适用场景:
- 数据量精确可控(如<2000条)
- 数据库支持大SQL(MySQL默认max_allowed_packet=4MB,可调大至16MB)
底层优化手段
关闭自动提交
// 必须手动控制事务 sqlSession.commit(); // 替代自动提交 connection.setAutoCommit(false); // 直接操作JDBC时
调整JDBC批量大小
Spring配置:
spring:
datasource:
hikari:
maximum-pool-size: 20
# 不推荐直接改批量参数,而是使用MyBatis的BATCH模式
直接操作JDBC:
PreparedStatement ps = conn.prepareStatement(sql);
conn.setAutoCommit(false);
for (User user : users) {
ps.setString(1, user.getName());
ps.addBatch();
if (i % 1000 == 0) {
ps.executeBatch(); // 每1000条执行一次
conn.commit();
}
}
ps.executeBatch();
conn.commit();
使用数据库原生批量特性
MySQL:
-- 关闭唯一索引检查(大数据量插入时临时启用) SET UNIQUE_CHECKS = 0; -- 关闭外键检查 SET FOREIGN_KEY_CHECKS = 0; -- 批量插入后恢复 SET UNIQUE_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;
PostgreSQL:
-- 使用COPY命令(极速) COPY user (name, age) FROM '/data/users.csv' DELIMITER ',';
完整优化策略代码示例
// 批量插入主方法
public void batchInsertUsers(List<User> userList) {
// 1. 按批次分割(防止内存溢出)
List<List<User>> batches = Lists.partition(userList, 500);
// 2. 使用BATCH模式执行
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
for (List<User> batch : batches) {
for (User user : batch) {
mapper.insert(user);
}
session.flushStatements(); // 非必要,但可提前释放
}
session.commit();
}
}
性能对比(100万条数据插入MySQL)
| 方法 | 耗时(秒) | 适用数据量 | 风险 |
|---|---|---|---|
| 逐条插入(默认SIMPLE) | 120+ | 小数据 | 高事务开销 |
| Batch Executor(500条/批) | 3-5 | 海量数据 | 低 |
| Foreach拼接(2000条/批) | 2-4 | 中型数据 | SQL过长风险 |
| JDBC原生Batch(1000条/批) | 3-5 | 通用 | 需手动管理 |
| LOAD DATA INFILE(MySQL) | 1-2 | 超大数据 | 文件格式限制 |
关键优化总结
- 永远不要用循环单条插入(除非测试数据<100条)
- 大数据量优先选择MyBatis的
ExecutorType.BATCH - 小批量(<1000条)可用
foreach,但需控制批次大小 - 始终手动关闭自动提交,在合适时机
commit - 监控数据库的
max_allowed_packet和连接池配置 - 对于极端性能需求,考虑直接使用
LOAD DATA或COPY命令
最佳实践:
// 通用方案:结合BATCH模式 + 分批次提交
@Transactional
public void insertBatch(List<T> data) {
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
// ... 处理逻辑
}
通过上述方法,MyBatis的批量插入性能可以从每分钟几千条提升到每秒数万条(取决于硬件和数据库配置)。