☰
Spring Boot+MyBatis-Plus批量插入性能优化实战
2026/10/29 17:55:03 网站建设 项目流程

1. 项目概述

最近在开发一个电商后台系统时,遇到了一个性能瓶颈:需要处理每天数十万条订单数据的批量插入。在使用Spring Boot + MyBatis-Plus的组合时,我发现批量插入的性能表现差异巨大,从每秒几十条到上万条都有可能。这让我意识到,批量插入这个看似简单的操作,实际上暗藏玄机。

2. 核心需求解析

2.1 为什么需要批量插入

在数据处理场景中,批量插入相比单条插入能带来显著的性能提升。以订单系统为例:

  • 减少网络开销:单条插入需要多次网络往返
  • 降低事务开销:批量操作可以合并为一个事务
  • 提高数据库效率:数据库引擎可以优化批量操作

2.2 技术选型考量

选择Spring Boot + MyBatis-Plus的组合主要基于:

  1. Spring Boot的自动配置简化了项目搭建
  2. MyBatis-Plus在MyBatis基础上提供了更便捷的CRUD操作
  3. 两者组合是国内Java开发的主流选择

3. 实现方案对比

3.1 原生JDBC批量插入

String sql = "INSERT INTO orders(...) VALUES(?,?,...)"; try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { conn.setAutoCommit(false); for (Order order : orders) { ps.setString(1, order.getOrderNo()); // 设置其他参数... ps.addBatch(); if (i % batchSize == 0) { ps.executeBatch(); } } ps.executeBatch(); conn.commit(); }

优点:

  • 性能最好
  • 控制粒度最细

缺点:

  • 代码冗长
  • 需要手动处理事务

3.2 MyBatis XML方式批量插入

<insert id="batchInsert" parameterType="java.util.List"> INSERT INTO orders(...) VALUES <foreach collection="list" item="item" separator=","> (#{item.orderNo},...) </foreach> </insert>

优点:

  • 代码简洁
  • 可读性好

缺点:

  • SQL长度可能超限
  • 事务管理仍需注意

3.3 MyBatis-Plus的saveBatch方法

orderService.saveBatch(orders, batchSize);

优点:

  • 使用最简单
  • 内置批处理逻辑

缺点:

  • 性能不如前两种
  • 黑盒操作,可控性差

4. 性能实测数据

在本地环境(MySQL 8.0,10000条数据)测试结果:

方案耗时(ms)内存占用(MB)
单条插入12500150
JDBC批量32050
MyBatis XML45060
MP saveBatch80070

5. 实战中的坑与解决方案

5.1 MySQL配置优化

# application.properties spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.minimum-idle=5 rewriteBatchedStatements=true

关键参数说明:

  • rewriteBatchedStatements:启用批量语句重写
  • 连接池大小需要合理设置

5.2 事务管理要点

@Transactional public void batchInsert(List<Order> orders) { // 分批处理 Lists.partition(orders, 1000).forEach(batch -> { orderMapper.batchInsert(batch); }); }

注意事项:

  • 避免一个大事务包含太多数据
  • 考虑使用编程式事务

5.3 内存溢出预防

处理大数据量时:

  1. 使用流式查询读取数据
  2. 分批处理,及时清空缓存
  3. 监控JVM内存使用

6. 高级优化技巧

6.1 多线程批量插入

ExecutorService executor = Executors.newFixedThreadPool(4); Lists.partition(orders, 2500).forEach(batch -> { executor.submit(() -> { orderService.saveBatch(batch); }); }); executor.shutdown();

注意点:

  • 线程池大小根据数据库连接池调整
  • 需要考虑数据顺序性要求

6.2 使用LOAD DATA INFILE

对于超大数据量:

String loadDataSql = "LOAD DATA LOCAL INFILE 'temp.csv' INTO TABLE orders..."; jdbcTemplate.execute(loadDataSql);

优势:

  • MySQL原生批量加载
  • 性能最优

限制:

  • 需要文件中间件
  • 权限配置复杂

7. 监控与调优

7.1 监控指标

关键监控点:

  • 批量插入耗时
  • 数据库CPU/IO使用率
  • JVM内存变化
  • 慢查询日志

7.2 性能分析工具

推荐工具:

  • Arthas:分析Java方法耗时
  • VisualVM:监控JVM状态
  • MySQL EXPLAIN:分析SQL执行计划

8. 最佳实践总结

经过多次实战验证,我的建议是:

  1. 中小数据量(<10万):MyBatis-Plus saveBatch + 合理批大小
  2. 大数据量(10万+):JDBC批量 + 多线程
  3. 超大数据量(百万+):考虑LOAD DATA INFILE

关键参数设置:

  • 批大小:500-2000为宜
  • 事务大小:每事务不超过5000条
  • 线程数:不超过连接池大小

最后提醒:任何批量操作都要考虑失败回滚方案,做好数据校验和日志记录。在实际项目中,我通常会先在小批量数据上测试,确认无误后再全量执行。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询