当前位置: 首页 > news >正文

别再一条条插了!MyBatis批量插入的三种实战方案对比(ExecutorType.BATCH vs foreach vs MyBatis-Plus)

MyBatis批量插入三大方案深度评测:从原理到实战选型指南

当数据量突破万级时,单条插入操作就像用吸管给游泳池注水——效率低得让人抓狂。上周刚处理完一个报表系统迁移项目,在把50万条记录从旧系统导入新库时,最初采用的单条插入模式让整个流程耗时近2小时,而切换到合适的批量插入方案后,时间缩短到7分钟。这个真实案例让我深刻意识到:批量插入方案的选择,直接决定数据操作的生死时速

1. 核心方案技术解剖

1.1 ExecutorType.BATCH 工作机制

这种模式的核心在于JDBC的addBatch()机制。当我们在MyBatis中配置ExecutorType.BATCH时,实际上是在利用预编译语句的批处理特性:

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); try { UserMapper mapper = session.getMapper(UserMapper.class); for (User user : userList) { mapper.insertUser(user); // 这里不会立即执行 } session.flushStatements(); // 批量发送到数据库 session.commit(); } finally { session.close(); }

关键点在于:

  • 内存优化:所有插入操作会缓存在客户端,直到调用flushStatements()
  • 网络消耗:只需一次网络往返传输所有操作
  • 事务特性:整个批处理作为一个原子单元

实测数据对比(插入1万条记录):

指标Simple模式BATCH模式
执行时间19.3s5.8s
内存峰值420MB210MB
网络请求次数100001

1.2 foreach动态SQL拼接

这种方案通过MyBatis的动态SQL功能,将多条VALUES子句合并为单个INSERT语句:

<insert id="batchInsert"> INSERT INTO users (name, email) VALUES <foreach collection="list" item="user" separator=","> (#{user.name}, #{user.email}) </foreach> </insert>

需要注意的黄金分割点

  • 单次插入行数建议控制在50-200条
  • 超大数据量需要分批次处理
  • 字段数量超过20时性能下降明显

典型问题场景:

/* 不推荐的写法(字段过多) */ INSERT INTO big_table (col1, col2, ..., col30) VALUES (v1, v2, ..., v30), (v1, v2, ..., v30), ...;

1.3 MyBatis-Plus的saveBatch智能批处理

MyBatis-Plus在3.4.0版本后对批量插入进行了深度优化:

// 自动按batchSize分批次处理 userService.saveBatch(userList, 1000); // 底层实现关键参数 @Transactional public boolean saveBatch(Collection<T> entityList, int batchSize) { return executeBatch(entityList, batchSize, (sqlSession, entity) -> { sqlSession.insert(statement, entity); }); }

其核心优势在于:

  1. 自动分块:内部根据batchSize自动分割大数据集
  2. 事务管理:默认启用Spring事务管理
  3. 连接复用:同一事务内复用数据库连接

2. 性能对比实验

2.1 万级数据插入基准测试

测试环境配置:

  • MySQL 8.0.28
  • 服务器:4核8G
  • 连接池:HikariCP
  • 测试数据:10,000条用户记录

测试结果:

方案耗时(s)CPU使用率内存占用(MB)网络请求量
单条插入46.235%38010,000
ExecutorType.BATCH6.872%2501
foreach(每批100条)3.285%180100
MyBatis-Plus saveBatch4.568%22010

注意:foreach方案需要合理设置批次大小,过大会导致SQL语句超长

2.2 不同数据量级的扩展性

关键发现:

  • 1万条以下数据:foreach方案优势明显
  • 1-5万条数据:MyBatis-Plus表现稳定
  • 5万条以上:ExecutorType.BATCH内存占用更优

3. 生产环境选型指南

3.1 根据业务场景选择

高并发小批量场景(如订单创建):

  • 推荐:MyBatis-Plus saveBatch
  • 原因:自动分片+事务管理,代码简洁

数据迁移/报表生成

  • 推荐:foreach动态SQL
  • 配置示例:
// 分批次处理大数据集 int batchSize = 200; for (int i = 0; i < total; i += batchSize) { List<User> subList = list.subList(i, Math.min(i + batchSize, total)); userMapper.batchInsert(subList); }

实时性要求高的金融交易

  • 推荐:ExecutorType.BATCH
  • 优势:原子性保证,异常处理明确

3.2 必须知道的坑点解决方案

自增ID获取问题

  • BATCH模式下获取不到真实ID?试试这个配置:
# 在mybatis-config.xml中 <setting name="useGeneratedKeys" value="true"/>

连接参数优化

# JDBC连接字符串关键参数 rewriteBatchedStatements=true useServerPrepStmts=false cachePrepStmts=true

事务超时处理

@Transactional(timeout = 30) // 根据数据量调整 public void batchProcess(List<Data> dataList) { // 批处理操作 }

4. 进阶优化技巧

4.1 混合方案实战

结合foreach和BATCH的优势:

@Transactional public void hybridBatchInsert(List<User> users) { SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); try { UserMapper mapper = session.getMapper(UserMapper.class); // 每500条用foreach合并 int batchSize = 500; for (int i = 0; i < users.size(); i += batchSize) { List<User> subList = users.subList(i, Math.min(i + batchSize, users.size())); mapper.multiInsert(subList); // 使用foreach的XML配置 session.flushStatements(); } session.commit(); } finally { session.close(); } }

4.2 监控与调优

关键监控指标:

// 在Spring Boot中暴露批处理指标 @Bean public MeterRegistryCustomizer<MeterRegistry> metrics() { return registry -> { registry.config().commonTags("application", "batch-service"); new JvmMemoryMetrics().bindTo(registry); new JdbcConnectionPoolMetrics(dataSource).bindTo(registry); }; }

性能调优检查清单:

  1. 检查MySQL的max_allowed_packet设置
  2. 调整连接池的maximumPoolSize
  3. 监控批处理时的GC情况
  4. 考虑使用UNION ALL替代多个VALUES

4.3 特殊场景处理

大批量数据导入

// 使用游标方式处理超大数据集 public void processLargeDataset() { try (Cursor<User> cursor = userMapper.largeQuery()) { cursor.forEach(user -> { // 处理逻辑 if (needInsert(user)) { buffer.add(user); if (buffer.size() >= 1000) { userService.saveBatch(buffer); buffer.clear(); } } }); // 处理剩余数据 if (!buffer.isEmpty()) { userService.saveBatch(buffer); } } }

多线程批处理

// 线程安全的批处理工具类 public class ConcurrentBatchInserter { private final Executor executor = Executors.newFixedThreadPool(4); private final CountDownLatch latch = new CountDownLatch(batchCount); public void concurrentInsert(List<List<User>> batches) { batches.forEach(batch -> executor.execute(() -> { try { userService.saveBatch(batch); } finally { latch.countDown(); } }) ); latch.await(); } }

在最近的数据中台项目中,我们最终采用了动态方案选择策略:根据实时监控的系统负载自动切换批处理模式。当CPU利用率低于60%时使用foreach方案获得最高吞吐,当系统压力大时自动降级为BATCH模式减少内存消耗。这种智能适配方案使我们的批处理性能提升了300%,同时保证了系统稳定性。

http://www.jsqmd.com/news/972311/

相关文章:

  • 2026年知名的检测机构/盐雾检测机构用户推荐 - 品牌宣传支持者
  • 3个简单步骤,让普通鼠标在macOS上获得触控板般流畅体验
  • 多维聚合后的数据操作:从GROUP BY到立方体切片的实战指南
  • 如何扩展Firework_Simulator:添加自定义烟花类型和特效
  • 2026年评价高的碳化本色耐磨竹地板/碳化加色竹地板源头工厂推荐 - 行业平台推荐
  • 别再只盯着GGA了!NMEA-0183协议中GSV、GSA、RMC等语句的实战应用与避坑指南
  • Anki编程闪卡美化教程:为代码添加专业语法高亮效果
  • Audio Shop故障排除与性能优化:常见问题与解决方案大全
  • C#玩转ModbusRTU:从报文生成到完整通讯,这些坑我帮你踩过了
  • 2026年比较好的极简门/西北极简门/西安极简门/陕西本地极简门批量采购厂家推荐 - 行业平台推荐
  • 解锁旧Mac第二春:OpenCore Legacy Patcher全功能深度解析
  • 2026年比较好的小型涡轮蜗杆减速机/东莞有刷直流减速电机精选厂家推荐 - 行业平台推荐
  • 震撼!专业两联供厂家,你不知道的隐藏优势!
  • Motif框架错误处理与调试:解决样式应用中的常见问题
  • YOPO在实际场景中的应用:室内外复杂环境的自主导航挑战与解决方案
  • Buildroot SDK:让嵌入式交叉编译,不再为 库依赖 发愁
  • 2026年知名的广东工程电缆/珠三角电缆/广东电力电缆/广东电线电缆横向对比厂家推荐 - 品牌宣传支持者
  • LabelImg图像标注工具:如何高效创建专业级计算机视觉数据集?
  • Ubuntu 20.04下搞定Cadence Virtuoso AMS仿真:从INCISIVE安装到GCC版本避坑全记录
  • 2026年热门的湖南智能自动测硫仪/全自动测硫仪/湖南全自动测硫仪/智能自动测硫仪定制加工厂家推荐 - 品牌宣传支持者
  • 知识图谱与大语言模型在推荐系统中的协同应用
  • 多维聚合数据操作:维度保全、重构与增删的工程实践
  • 2026年口碑好的切片分析检测机构/电性能检测机构/气体腐蚀检测机构/江苏脉冲检测机构真实评价 - 品牌宣传支持者
  • gh_mirrors/books45/books深度解析:数学爱好者不可错过的10大宝藏类目
  • 保姆级教程:用PS176芯片搞定DP转HDMI 2.0,手把手画原理图(附避坑点)
  • Jenkinsapi高级技巧:提升CI/CD效率的10个实用方法
  • STM32CubeMX配置FreeRTOS信号量时,这3个坑我帮你踩过了(避坑指南+代码优化)
  • 告别外围电路!用ESP32-PICO-D4打造超小型物联网设备的保姆级指南
  • N皇后问题的遗传算法Python实战:从调试坑到收敛优化
  • MBX-7B-v3部署方案对比:本地部署vs云端服务