侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

MyBatis批量插入性能优化与实现方案

2025-12-5 / 0 评论 / 4 阅读

题目

MyBatis批量插入性能优化与实现方案

信息

  • 类型:问答
  • 难度:⭐⭐

考点

动态SQL,批量操作,性能优化,执行器原理

快速回答

当使用MyBatis的标签进行大批量数据插入时,主要面临两个问题:

  1. SQL语句过长:拼接的SQL可能超出数据库限制(如MySQL的max_allowed_packet)
  2. 性能低下:单条超长SQL解析执行效率远低于批量提交

优化方案:

  • 使用ExecutorType.BATCH模式配合sqlSession.flushStatements()
  • 设置rewriteBatchedStatements=true(MySQL)启用真正的批量操作
  • 分批次提交(建议每1000条提交一次)
## 解析

问题现象与原理说明

当使用如下动态SQL进行批量插入时:

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

随着数据量增大(如10万条),会产生:

  1. SQL长度问题:拼接后的SQL可能长达几MB,超过数据库配置的max_allowed_packet
  2. 性能瓶颈
    • 数据库解析巨型SQL效率低下
    • 事务锁定时间过长
    • 内存占用过高可能导致OOM

优化方案与代码实现

方案1:BatchExecutor批量模式(推荐)

try (SqlSession sqlSession = sqlSessionFactory
    .openSession(ExecutorType.BATCH)) {
  UserMapper mapper = sqlSession.getMapper(UserMapper.class);

  for (int i = 0; i < dataList.size(); i++) {
    mapper.insertSingle(dataList.get(i));
    // 每1000条提交一次
    if (i % 1000 == 0 || i == dataList.size() - 1) {
      sqlSession.flushStatements();
    }
  }
  sqlSession.commit();
}

关键配置:

  • MySQL连接串添加rewriteBatchedStatements=true
  • MyBatis映射文件使用单条插入语句:
    <insert id="insertSingle">
      INSERT INTO users(name, email) 
      VALUES(#{name}, #{email})
    </insert>

方案2:分批次使用<foreach>

int batchSize = 1000;
for (int i = 0; i < dataList.size(); i += batchSize) {
  List<User> subList = dataList.subList(i, Math.min(i + batchSize, dataList.size()));
  userMapper.batchInsert(subList); // 使用原动态SQL但每次只传1000条
}

最佳实践

  1. 批处理大小:根据数据库性能调整(通常500-2000条/批)
  2. 事务控制:整个批量操作应在同一事务中
  3. 连接池配置:增加最大连接数应对并发批量操作
  4. 监控指标:关注BatchExecutorbatchupdate调用次数

常见错误

  • 忘记提交事务:Batch模式需要手动commit()
  • 缺少flushStatements:导致所有语句缓存在内存未执行
  • 未启用MySQL批量重写:未配置rewriteBatchedStatements=true实际仍是单条插入
  • 超大事务:未分批次提交导致事务过长

扩展知识

  • Executor原理
    • SIMPLE:默认模式,每条语句单独执行
    • BATCH:缓存多个PreparedStatement批量提交
    • REUSE:复用预处理语句
  • JDBC优化
    • addBatch():将语句添加到批处理
    • executeBatch():执行批处理命令
  • 性能对比(10万条数据测试):
    方式耗时内存占用
    巨型<foreach>25s1.2GB
    BatchExecutor(无rewrite)18s300MB
    BatchExecutor(有rewrite)3s50MB