Mybatis 大数据量批量写优化的案例详解

 更新时间:2023年05月01日 09:00:09   作者:遇见的昨天  
这篇文章主要介绍了Mybatis 大数据量批量写优化的示例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

Mybatis 大数据量批量写优化

在项目中使用批量数据插入,经常会用到 mybatis的 foreach,如下:

<insert id="batchInsert" parameterType="java.util.List">
    insert into USER (id, name) values
    <foreach collection="list" item="model" index="index" separator=","> 
        (#{model.id}, #{model.name})
    </foreach>
</insert>

就是将

INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");

转换成

INSERT INTO `table1` (`field1`, `field2`) 
VALUES ("data1", "data2"),
("data1", "data2"),
("data1", "data2"),
("data1", "data2"),
("data1", "data2");

从理论上将,复用conn,将多次io,转换成一次io,应该是提升效率的。但是实际上当数据量比较大的时候,用foreach效率非常低,速度非常慢

当表的列数较多(20+),以及一次性插入的行数较多(5000+)时,整个插入的耗时十分漫长,达到了14分钟,这是不能忍的

那为什么使用使用foreach效率如此之低呢??

Mybatis默认执行器类型为Simple,默认会为每一个sql产生一个PrepareStatement,而且对于foreach无法使用缓存。如果字段和行数非常多,那么sql必然也会很长,占位符也会非常多,除此之外还要建立占位符和参数之间的映射,那么解析时间必然会长。因此如果values行数越多,那么解析时间必然很长。执行效率低。

在这里插入图片描述


如果非要使用 foreach 的方式来进行批量插入的话,可以考虑减少一条 insert 语句中 values 的个数,最好能达到上面曲线的最底部的值,使速度最快。一般按经验来说,一次性插20~50行数量是比较合适的,时间消耗也能接受。

那么如果要用批量插入,改如何优化呢?

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class);
    List<SimpleTableRecord> records = getRecordsToInsert(); // not shown
    BatchInsert<SimpleTableRecord> batchInsert = insert(records)
            .into(simpleTable)
            .map(id).toProperty("id")
            .map(firstName).toProperty("firstName")
            .map(lastName).toProperty("lastName")
            .map(birthDate).toProperty("birthDate")
            .map(employed).toProperty("employed")
            .map(occupation).toProperty("occupation")
            .build()
            .render(RenderingStrategy.MYBATIS3);
    batchInsert.insertStatements().stream().forEach(mapper::insert);
    session.commit();
} finally {
    session.close();
}

基本思想是将 MyBatis session 的 executor type 设为 Batch ,然后通过遍历多次执行插入语句

就类似于JDBC的下面语句一样。

Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true","root","root");
connection.setAutoCommit(false);
PreparedStatement ps = connection.prepareStatement(
        "insert into tb_user (name) values(?)");
for (int i = 0; i < stuNum; i++) {
    ps.setString(1,name);
    ps.addBatch();
}
ps.executeBatch();
connection.commit();
connection.close();

附录:Mybatis批量处理优化

Mybatis内置的ExecutorType有3种,默认的是simple单句模式,该模式下它为每个语句的执行创建一个新的预处理语句,单句提交sql;batch模式重复使用已经预处理的语句,并且批量执行所有语句,大批量模式下性能更优。

请注意batch模式在Insert操作时事务没有提交之前,是没有办法获取到自增的id,所以请根据业务情况使用。
使用simple模式提交10000条数据,时间为19s,batch模式为6s ,大致情况如此,优化的具体还要看提交的语句情况。
如果需要使用 foreach来优化数据插入的话,需要将每次插入的记录控制在 10-100 左右是比较快的,建议每次100来分割数据,也就是分而治之思想。

普通插入

默认的插入方式是遍历insert语句,单条执行,效率肯定低下,如果成堆插入,更是性能有问题。

INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");

foreach 优化插入

如果要优化插入速度时,可以将许多小型操作组合到一个大型操作中。理想情况下,这样可以在单个连接中一次性发送许多新行的数据,并将所有索引更新和一致性检查延迟到最后才进行。

<insert id="batchInsert" parameterType="java.util.List">
    insert into table1 (field1, field2) values
    <foreach collection="list" item="t" index="index" separator=","> 
        (#{t.field1}, #{t.field2})
    </foreach>
</insert>

翻译成sql语句也就是

INSERT INTO `table1` (`field1`, `field2`) 
VALUES ("data1", "data2"),
("data1", "data2"),
("data1", "data2"),
("data1", "data2"),
("data1", "data2");

到此这篇关于Mybatis 大数据量批量写优化的文章就介绍到这了,更多相关Mybatis 批量写优化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis-Plus雪花id的使用以及解析机器ID和数据标识ID实现

    Mybatis-Plus雪花id的使用以及解析机器ID和数据标识ID实现

    这篇文章主要介绍了Mybatis-Plus雪花id的使用以及解析机器ID和数据标识ID实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • java使用ajax完成上传文件

    java使用ajax完成上传文件

    这篇文章主要为大家详细介绍了java使用ajax完成上传文件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • Java正则表达式工具方法汇总

    Java正则表达式工具方法汇总

    这篇文章主要介绍了Java正则表达式工具方法汇总,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • 详解springboot项目启动时如何排除用不到的bean

    详解springboot项目启动时如何排除用不到的bean

    使用springboot开发项目,我们有时候会排除一些项目里面用不到的bean,不然的话项目启动会报错,这种情况通常是发生在什么场景里呢,以及如何解决呢,今天咱们就聊一聊
    2024-01-01
  • 详解Spring事件发布与监听机制

    详解Spring事件发布与监听机制

    Spring提供了ApplicationContext事件机制,可以发布和监听事件,这个特性非常有用。Spring内置了一些事件和监听器,例如在Spring容器启动前,Spring容器启动后,应用启动失败后等事件发生后,监听在这些事件上的监听器会做出相应的响应处理
    2021-06-06
  • PostMan如何传参给@RequestBody(接受前端参数)

    PostMan如何传参给@RequestBody(接受前端参数)

    这篇文章主要介绍了PostMan如何传参给@RequestBody(接受前端参数),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • SpringBoot启动自动终止也不报错的原因及解决

    SpringBoot启动自动终止也不报错的原因及解决

    这篇文章主要介绍了SpringBoot启动自动终止也不报错的原因及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • JUC之Semaphore源码分析

    JUC之Semaphore源码分析

    这篇文章主要为大家详细分析了JUC之Semaphore源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Java实现常见排序算法的优化

    Java实现常见排序算法的优化

    今天给大家带来的是关于Java的相关知识,文章围绕着Java实现常见排序算法的优化展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-01-01
  • java分布式面试接口如何保证幂等及概念理解

    java分布式面试接口如何保证幂等及概念理解

    这篇文章主要为大家介绍了java分布式面试中接口如何保证幂等的问题解答以及概念描述,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-03-03

最新评论