MyBatisPlus批量插入性能陷阱揭秘rewriteBatchedStatements的实战配置上周排查一个数据导入模块的性能问题时发现一个令人震惊的事实——我们团队使用了三年的MyBatisPlus批量插入方法saveBatch竟然一直在用单条SQL循环执行这个发现直接导致我们的批量插入性能比预期慢了10倍以上。本文将完整还原这个排查过程并给出具体的解决方案。1. MyBatisPlus批量插入的真相大多数Java开发者在使用MyBatisPlus时都会自然地认为saveBatch方法就是用来做批量插入的。但实际情况是在不做特殊配置的情况下saveBatch只是循环执行单条INSERT语句。我们来看一个简单的性能对比测试// 测试代码示例 ListUser userList generateTestData(10000); long start System.currentTimeMillis(); userService.saveBatch(userList); System.out.println(耗时 (System.currentTimeMillis() - start) ms);在不配置rewriteBatchedStatements的情况下插入1万条数据可能需要5-8秒而正确配置后同样的数据量只需要500-800毫秒。这种性能差异在大数据量场景下会变得极其明显。注意即使使用了saveBatch方法如果没有正确配置MySQL连接参数控制台日志中你看到的仍然是大量单条INSERT语句。2. 配置rewriteBatchedStatements参数要让MyBatisPlus真正执行批量插入关键是在MySQL JDBC连接字符串中添加rewriteBatchedStatementstrue参数。以下是完整的配置方式# application.yml配置示例 spring: datasource: url: jdbc:mysql://localhost:3306/your_db?rewriteBatchedStatementstrueuseSSLfalseserverTimezoneUTC username: your_username password: your_password driver-class-name: com.mysql.cj.jdbc.Driver配置生效后MyBatisPlus会将批量插入优化为真正的批量SQL语句格式类似于INSERT INTO user (name, age) VALUES (?, ?), (?, ?), (?, ?);而不是多条单条INSERT语句。这种批处理方式大幅减少了网络往返和SQL解析开销。3. 验证配置是否生效配置完成后我们需要验证rewriteBatchedStatements是否真正生效。以下是几种验证方法查看执行日志正确配置后日志中应该显示批量INSERT语句而非单条INSERT性能对比测试相同数据量下配置前后应该有5-10倍的性能差异使用MySQL通用日志在MySQL服务器端开启通用查询日志观察实际执行的SQL这里提供一个简单的验证代码片段SpringBootTest public class BatchInsertTest { Autowired private UserService userService; Test public void testBatchInsertPerformance() { ListUser userList generateTestData(10000); // 第一次执行可能有JVM预热影响 userService.saveBatch(userList); // 正式测试 long start System.currentTimeMillis(); userService.saveBatch(userList); long duration System.currentTimeMillis() - start; System.out.println(插入10000条数据耗时 duration ms); assertTrue(duration 1000, 批量插入性能不达标); } }4. 批量插入的注意事项即使配置了rewriteBatchedStatements还有一些特殊情况会导致MyBatisPlus退化为单条插入实体类字段为null如果批量插入的实体类中有任何字段为null且没有配置忽略策略整个批量操作会退化为单条插入混合操作批量操作中同时包含插入和更新时可能无法使用批量优化特定版本问题某些旧版本的MyBatisPlus或MySQL驱动可能存在兼容性问题针对字段为null的问题可以通过以下方式解决// 使用FieldStrategy.IGNORED忽略null值检查 TableField(insertStrategy FieldStrategy.IGNORED) private String optionalField; // 或者使用自动填充功能 TableField(fill FieldFill.INSERT) private Date createTime;5. 高级优化技巧除了基本的rewriteBatchedStatements配置外还有几个可以进一步提升批量插入性能的技巧合理设置batchSizeMyBatisPlus默认的batchSize是1000可以根据实际情况调整// 自定义batchSize userService.saveBatch(userList, 500);使用并行流处理对于超大数据集可以考虑使用并行流分割处理Lists.partition(userList, 1000).parallelStream() .forEach(batch - userService.saveBatch(batch));调整MySQL服务器参数适当增大max_allowed_packet等参数考虑使用LOAD DATA INFILE对于超大规模数据导入直接使用MySQL的LOAD DATA命令可能更高效6. 性能对比数据以下是我们在测试环境中得到的性能对比数据插入10万条记录配置方式耗时(ms)网络请求次数SQL语句数量默认配置5200100000100000仅rewriteBatchedStatements850100100rewriteBatchedStatements优化batchSize6205050并行处理所有优化3802020从数据可以看出正确的配置可以带来数量级的性能提升。在我们的生产环境中一个原本需要10分钟的数据导入任务优化后只需要不到1分钟就完成了。