【Mybatis】MyBatisPlus的saveBatch真的是批量插入吗?深度解析与性能优化

前言

在使用MyBatis-Plus进行批量数据插入时,许多开发者会发现:即使调用saveBatch方法,数据库仍会产生大量INSERT语句 。本文将深入源码揭示背后的真相,并提供3种性能优化方案,让你的批量插入速度提升10倍!


一、为什么批量插入这么慢?

1.1 性能测试对比

java 复制代码
// 测试代码
List<User> users = generateUsers(10000); // 生成1w条测试数据
long start = System.currentTimeMillis();
userService.saveBatch(users);
System.out.println("耗时:" + (System.currentTimeMillis() - start) + "ms");

// 测试结果
// 默认配置:耗时 3200ms
// 期待效果:耗时 < 500ms

1.2 SQL监控日志

sql 复制代码
-- 实际执行的SQL
INSERT INTO user (name,age) VALUES ('user1', 20);
INSERT INTO user (name,age) VALUES ('user2', 21);
...(重复1w次)

二、源码解析:揭开saveBatch的真面目

2.1 核心源码追踪

代码定位 :ServiceImpl.saveBatch → SqlHelper.executeBatch

java 复制代码
// 关键源码片段
public boolean saveBatch(Collection<T> entityList, int batchSize) {
    return executeBatch(entityList, batchSize, (sqlSession, entity) -> 
        sqlSession.insert(sqlStatement, entity)); // 逐条插入
}

2.2 执行流程图示

saveBatch调用 获取SqlSession 循环实体列表 单条插入 提交事务

2.3 慢速根源分析

关键因素 影响说明
SimpleExecutor 默认执行器逐条提交SQL
事务提交机制 默认自动提交(可优化)
JDBC网络开销 每次插入产生一次网络IO

三、性能优化方案

3.1 方案一:启用批量执行器(配置优化)

3.1.1 修改配置

yaml 复制代码
mybatis-plus:
  configuration:
    default-executor-type: batch # 启用批量模式
  global-config:
    db-config:
      logic-delete-field: isDeleted # 避免逻辑删除干扰

3.1.2 效果验证

sql 复制代码
-- 批量插入SQL(真实执行)
INSERT INTO user (name, age) 
VALUES ('user1',20), ('user2',21)...;

性能提升:1w条数据插入从3200ms → 850ms


3.2 方案二:自定义批量SQL(终极优化)

3.2.1 扩展Mapper接口

java 复制代码
public interface UserMapper extends BaseMapper<User> {
    @Insert("<script>" +
            "INSERT INTO user (name, age) VALUES " +
            "<foreach collection='list' item='item' separator=','>" +
            "(#{item.name}, #{item.age})" +
            "</foreach>" +
            "</script>")
    void insertBatch(@Param("list") List<User> users);
}

3.2.2 服务层调用

java 复制代码
@Autowired
private UserMapper userMapper;

public void superBatchSave(List<User> users) {
    userMapper.insertBatch(users);
}

性能对比:1w条数据插入仅需420ms


3.3 方案三:事务+分批提交(平衡方案)

java 复制代码
@Transactional
public void batchSave(List<User> users) {
    int batchSize = 1000;
    for (int i = 0; i < users.size(); i += batchSize) {
        List<User> subList = users.subList(i, Math.min(i + batchSize, users.size()));
        userService.saveBatch(subList);
    }
}

优势 :

✅ 避免大事务导致锁表

✅ 内存占用可控

✅ 兼容默认实现


四、生产环境注意事项

4.1 连接池配置

yaml 复制代码
spring:
  datasource:
    hikari:
      maximum-pool-size: 20 # 根据并发量调整
      connection-timeout: 60000

4.2 监控指标

监控项 推荐阈值 工具
批量插入耗时 < 1s/千条 Grafana + Prometheus
数据库连接数 < 80%最大连接数 Druid监控
事务锁等待时间 < 500ms SHOW ENGINE INNODB STATUS

4.3 失败重试机制

java 复制代码
@Retryable(value = SQLException.class, maxAttempts = 3)
public void batchOperation() {
    // 批量操作
}

五、性能对比总结

方案 1w条耗时 网络请求次数 代码侵入性 适用场景
默认saveBatch 3200ms 10000 无 小数据量场景
BatchExecutor 850ms 1 低 中大数据量
自定义批量SQL 420ms 1 高 极致性能要求
事务分批提交 1500ms 10 中 平衡型方案
相关推荐
2301_7807896610 小时前
跨平台适配!SDK 游戏盾兼容全终端游戏场景
java·网络·ddos
学长毕设11 小时前
基于springboot的食品仓库管理系统
java·spring boot·后端
滴滴答答哒11 小时前
ASP.NET Core + FaceRecognitionDotNet 人脸识别实战:DI 单例、特征向量持久化与 1:N 比对
后端·asp.net
步行cgn11 小时前
FormHttpMessageConverter 详解:Spring MVC 中表单数据的读写转换器
java·spring·mvc
龙亘川11 小时前
构建城市公共安全数字底座:亘川智城智慧安防系统全维度解读
java·大数据·开发语言·人工智能·信息可视化
金銀銅鐵11 小时前
[Java] 用 CLI 展示 EnumMap 增删键值对时的内部变化
java·源码·ai编程
秋天的怀念_David12 小时前
第二十二夜 · 数字迷雾
java·程序人生·算法
Raas10012 小时前
AI网关部署方案?从部署到上线全流程
java·开发语言·人工智能·gateway·ai网关·企业级ai网关·企业级ai产品
niucloud-admin13 小时前
NIUCLOUD PHP SAAS上门家政使用文档:城市管理
java·前端·php
霸道流氓气质13 小时前
智能客服组件:分层意图识别、RAG Advisor 与人工转接状态机的 Vue3 + Spring AI 生产级实战
java·人工智能·spring