news 2026/6/23 10:34:01

别再一条条插了!MyBatis批量插入数据,用ExecutorType.BATCH到底能快多少?(附Spring Boot实战代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再一条条插了!MyBatis批量插入数据,用ExecutorType.BATCH到底能快多少?(附Spring Boot实战代码)

MyBatis批量插入性能深度评测:从原理到实战的全面优化指南

在处理海量数据入库时,开发者常会遇到性能瓶颈。上周我负责一个用户行为分析系统,需要将300万条日志数据写入MySQL,最初采用单条插入方式耗时近2小时,经过优化后缩短到7分钟。本文将分享这段实战经验,通过量化对比帮助开发者选择最优批量插入方案。

1. 批量插入技术全景图

MyBatis提供了多种批量数据插入方式,每种方法在实现原理和适用场景上存在显著差异。理解这些差异是进行性能优化的第一步。

核心机制对比:

插入方式原理描述事务控制预编译语句复用
默认Simple模式每条SQL独立执行,自动提交或跟随事务每条语句独立事务或统一
foreach拼接将多条values合并为单个INSERT语句单语句事务部分
ExecutorType.BATCH复用PreparedStatement,批量发送执行命令统一事务
MyBatis-Plus的saveBatch基于BATCH模式封装,自动分片处理统一事务

关键发现:BATCH模式通过JDBC的addBatch()机制,将多次网络往返优化为单次批量传输,这是性能提升的关键

在实际项目中,我曾遇到一个典型场景:需要将CSV文件中的50万条设备数据导入数据库。最初使用Simple模式耗时约45分钟,切换到BATCH模式后仅需4分钟,效果立竿见影。

2. 性能基准测试:数据不说谎

为了量化不同方案的性能差异,我们搭建了标准测试环境:

  • 硬件:4核CPU/8GB内存
  • 数据库:MySQL 8.0 with rewriteBatchedStatements=true
  • 测试工具:JMH基准测试
  • 数据量级:1k/10k/100k条记录

测试结果对比(单位:毫秒):

| 数据量 | Simple模式 | foreach(100) | BATCH模式 | saveBatch | |--------|------------|--------------|-----------|-----------| | 1,000 | 4,200 | 850 | 620 | 680 | | 10,000 | 41,800 | 3,200 | 2,800 | 3,100 | | 100,000| 超时 | 29,500 | 24,300 | 26,800 |

内存占用方面,BATCH模式表现出色:

  • Simple模式:持续增长,峰值达1.2GB
  • BATCH模式:稳定在300MB左右
// JMH测试代码片段示例 @State(Scope.Thread) public class MyBatisBenchmark { private SqlSessionFactory sqlSessionFactory; @Setup public void init() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Benchmark public void testBatchInsert() { try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper mapper = session.getMapper(UserMapper.class); for (int i = 0; i < 10000; i++) { mapper.insert(new User("user"+i, "email"+i)); } session.commit(); } } }

3. 实战优化技巧与避坑指南

3.1 关键参数配置

MySQL的rewriteBatchedStatements参数对性能影响巨大:

# application.properties spring.datasource.url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useSSL=false

这个配置让JDBC驱动将多个INSERT语句重写为多值形式,在我的测试中,开启后性能提升约40%。

3.2 事务管理的正确姿势

BATCH模式必须配合正确的事务管理:

// 错误示例:自动提交模式 try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper mapper = session.getMapper(UserMapper.class); for (User user : userList) { mapper.insert(user); // 每次insert都会立即执行 } // 忘记flush和commit } // 正确做法 try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper mapper = session.getMapper(UserMapper.class); for (int i = 0; i < userList.size(); i++) { mapper.insert(userList.get(i)); if (i % 1000 == 0) { session.flushStatements(); // 定期刷新,避免OOM } } session.commit(); // 最终提交 }

3.3 分片策略优化

对于超大数据量(百万级),建议采用分片处理:

public void batchInsertInChunks(List<User> users, int chunkSize) { SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); try { UserMapper mapper = session.getMapper(UserMapper.class); List<List<User>> chunks = Lists.partition(users, chunkSize); for (List<User> chunk : chunks) { for (User user : chunk) { mapper.insert(user); } session.flushStatements(); session.clearCache(); // 防止缓存堆积 } session.commit(); } finally { session.close(); } }

4. 高级应用场景解析

4.1 与Spring事务集成

在Spring环境中使用BATCH模式需要特别注意:

@Transactional public void batchProcess(List<Data> dataList) { // 获取当前SqlSession并切换执行器类型 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager .getResource(sqlSessionFactory); holder.getSqlSession().flushStatements(); // 实际批处理操作 dataList.forEach(data -> repository.save(data)); }

4.2 MyBatis-Plus的最佳实践

MyBatis-Plus的saveBatch方法底层仍使用BATCH模式,但提供了更友好的API:

// 自动分批,默认批次大小1000 userService.saveBatch(userList); // 自定义批次大小 userService.saveBatch(userList, 500); // 事务中的批量保存 @Transactional public void processUsers(List<User> users) { users.forEach(user -> { // 一些业务处理 user.setStatus("PROCESSED"); }); userService.saveBatch(users); }

在最近的项目中,我们对比发现:

  • 原生BATCH模式:代码更灵活,适合复杂业务逻辑
  • saveBatch方法:开发效率高,适合标准CRUD操作

5. 性能优化深度策略

5.1 连接池配置建议

# application.yml spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000

合理的连接池配置可以避免资源竞争:

  • 批量操作时适当增大maximum-pool-size
  • 调整超时时间适应长时间批量任务

5.2 数据库端优化

-- 批量插入前临时调整参数 SET unique_checks=0; SET foreign_key_checks=0; SET sql_log_bin=0; -- 批量操作结束后恢复 SET unique_checks=1; SET foreign_key_checks=1; SET sql_log_bin=1;

这些设置在我的一个数据迁移项目中,使性能提升了约25%。但要注意:

  • 只适合数据迁移等特殊场景
  • 必须确保数据一致性不受影响

5.3 监控与调优工具

推荐使用Arthas监控MyBatis执行情况:

# 监控Mapper方法调用 watch com.example.mapper.UserMapper insert '{params,returnObj}' -x 3 # 查看SQL执行时间 trace com.example.mapper.UserMapper insert

在压力测试时,我们发现当单批次超过5万条时,性能开始下降。最终确定2万条为最佳批次大小,这个经验值可能随硬件配置而变化。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 6:41:42

如何从微信聊天记录中挖掘人生宝藏:WeChatMsg完全指南

如何从微信聊天记录中挖掘人生宝藏&#xff1a;WeChatMsg完全指南 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeCha…

作者头像 李华
网站建设 2026/6/14 6:41:59

14【.NET10 实战--孢子记账--产品智能化】--智能生成预算

经过前面三篇文章的铺垫&#xff0c;我们先后完成了硅基流动平台的接入配置、大模型接入方式的技术选型&#xff0c;以及 LLM 调用层的统一封装&#xff0c;孢子记账的智能化基础设施已经基本就绪。从现在开始&#xff0c;我们的目光将从"怎么接入 AI"转向"用 A…

作者头像 李华