MyBatis 用 foreach 批量插入,6000 条数据花了 15 分钟,如何优化?

MyBatis 用 foreach 批量插入代码如下:

 <insert id="batchInsert" parameterType="java.util.List">

       insert into Test(id, username,password) values

      <foreach collection="list" item="userList" index="index" separator=",">

      (#{userList.id}, #{userList.username}, #{userList.password})

      </foreach>

</insert>

使用以上代码实操结果:当一次性插入的列数达20+,行数达6000+时,整个插入的耗时花了15分钟。

 

原因:

默认执行器类型为Simple,会为每个语句创建一个新的预处理语句,即创建一个PreparedStatement对象;

MyBatis对于含有foreach的语句,无法采用缓存,那么在每次调用方法时,都会重新解析sql语句;

由于foreach后有6000+个values,PreparedStatement特别长,包含了很多占位符,对于占位符和参数的映射尤其耗时。

 

突破点:

在MySql Docs中提到,优化插入速度时,可以将多个小型操作组合到一个大型操作中;

在理想情况下,在单个连接中一次性发送多行的新数据,并将所有索引更新和一致性检查延迟到最后才进行;

当插入数量很多时,不能一次性全放在一条语句里。

 

解决办法:

 1、如果非要用foreach的方式来插入,一次性插20~50行数比较合适。

 2、MyBatis推荐的批量插入方式ExecutorType.BATCH (类似JDBC的ps.executeBatch() ),参考代码如下:


SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
     SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class);
     List<SimpleTableRecord> records = getRecordsToInsert();
     BatchInsert<SimpleTableRecord> batchInsert = insert(records)
          .into(simpleTable).map(id).toProperty("id")
          .map(username).toProperty("username")
          .map(password).toProperty("password")
          .build().render(RenderingStrategy.MYBATIS3);
    batchInsert.insertStatements().stream().forEach(mapper::insert);
    session.commit();
}catch (Exception e) {

}finally {

session.close();

}

 

 

posted @ 2022-08-26 11:57  海盗哥哥  阅读(1377)  评论(0编辑  收藏  举报