MyBatis批量插入的3种方案对比,速度差10倍!
你还在用for循环做MyBatis批量插入? 本文通过真实案例+代码实测,对比三种批量插入方案的速度差异与实现原理。文末附实测数据截图,看完立省80%数据库操作时间!
一、青铜方案:循环单条插入(性能灾难)
// 典型错误示例
for (User user : userList) {
userMapper.insert(user);
}
特点:
- 每条insert语句单独执行
- 每次请求都经历完整JDBC流程
- 事务自动提交(除非手动控制)
实测结果(1万条数据):
- 耗时:约30秒
- 优点:代码简单
- 缺点:产生1万次网络IO,99%的时间浪费在连接开销
二、白银方案:BatchExecutor批处理(性能提升10倍)
// 开启批处理模式
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (User user : userList) {
mapper.insert(user);
if(i % 1000 == 0){
sqlSession.flushStatements(); // 分段提交
}
}
sqlSession.commit();
核心原理:
- 通过ExecutorType.BATCH启用JDBC批处理
- 将多个SQL语句打包发送
- 事务统一提交
实测结果(1万条数据):
- 耗时:约3秒
- 优点:JDBC层优化,网络IO减少90%
- 缺点:需要手动控制提交时机
三、王者方案:动态SQL拼接(极限性能)
<!-- XML动态SQL -->
<insert id="batchInsert">
INSERT INTO user (name,age) VALUES
<foreach collection="list" item="item" separator=",">
(#{item.name}, #{item.age})
</foreach>
</insert>
执行特点:
- 生成单条INSERT INTO ... VALUES (...),(...)语句
- 数据库单次解析执行
- 利用数据库批量写入优化
实测结果(1万条数据):
- 耗时:约1秒
- 优点:理论最快执行方式
- 致命缺陷:SQL长度限制(MySQL默认4MB)
全方位对比表
维度 | 循环单条插入 | BatchExecutor | 动态SQL拼接 |
执行方式 | N条独立SQL | JDBC批处理 | 单条大SQL |
网络IO次数 | N次 | 1次 | 1次 |
事务控制 | 自动提交 | 手动提交 | 单事务 |
内存消耗 | 低 | 中 | 高(SQL缓存) |
可读性 | ★★★★ | ★★★ | ★★ |
适用场景 | <100条 | 1k-10w级 | <5k条(防溢出) |
实测数据验证(1万条记录)
避坑指南
- 动态SQL长度陷阱:MySQL的max_allowed_packet参数限制单条SQL大小
- 批处理内存溢出:每5千条左右建议执行flushStatements()
- 事务一致性:BatchExecutor必须显式commit才会生效
- 主键回写:动态SQL方案无法获取全部自增ID
选型建议
- 小数据量(<500条):动态SQL方案最优
- 常规批量(1k-5w条):BatchExecutor最稳妥
- 超大数据量(>10w条):结合BatchExecutor+分段提交
终极技巧:生产环境推荐组合方案:BatchExecutor + 分批次提交 + 动态SQL长度控制,实测可提升百倍性能!
思考题:为什么动态SQL方案在Oracle数据库中反而可能变慢?欢迎评论区讨论!(提示:与SQL解析机制有关)