首页 > 其他分享 >MyBatis批量插入数据优化

MyBatis批量插入数据优化

时间:2024-01-06 12:33:47浏览次数:31  
标签:statement 批量 mybatis 插入 plus student MyBatis stopWatch

背景介绍

我们使用了 mybatis-plus 框架,并采用其中的 saveBatch 方法进行批量数据插入。然而,通过深入研究源码,我发现这个方法并没有如我期望的那样高效

MyBatis批量插入数据优化_mybatis 批量处理

这是因为最终在执行的时候还是通过for循环一条条执行insert,然后再一批的进行flush ,默认批的消息为1000

MyBatis批量插入数据优化_mybatis 批量处理_02

为了找到更优秀的解决方案,我展开了一场性能优化的探索之旅。好了我们现在开始探索

实验准备

  • 创建一张表tb_student
create table springboot_mp.tb_student
(
    id      bigint auto_increment comment '主键ID'
        primary key,
    stuid   varchar(40)   not null comment '学号',
    name    varchar(30)   null comment '姓名',
    age     tinyint       null comment '年龄',
    sex     tinyint(1)    null comment '性别 0 男 1 女',
    dept    varchar(2000) null comment '院系',
    address varchar(400)  null comment '家庭地址',
    constraint stuid
        unique (stuid)
);
  • 创建spring-boot-mybatis-demo项目并在pom.xml中添加依赖

MyBatis批量插入数据优化_mybatis 批量处理_03

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>


    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3</version>
    </dependency>


    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
  • application.yml配置
server:
  port: 8890

spring:
  application:
    name: mybatis-demo #指定服务名
  datasource:
    username: root
    password: root
#    url: jdbc:mysql://localhost:3306/springboot_mp?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
    url: jdbc:mysql://localhost:3306/springboot_mp?useUnicode=true&characterEncoding=utf8
    driver-class-name: com.mysql.cj.jdbc.Driver
#
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath*:/mapper/**/*.xml
  • 使用mybatisX生成代码

MyBatis批量插入数据优化_mybatis 批量处理_04

MyBatis批量插入数据优化_mybatis 批量处理_05

MyBatis批量插入数据优化_mybatis 批量处理_06

探索实验

每次都是插入100000条数据

注意:因为我的电脑性能比较好,所以才插入这么多数据,大家可以插入1000进行实验对比


  1. 单条循环插入:传统方法的基准

首先,我采用了传统的单条循环插入方法,将每条数据逐一插入数据库,作为性能对比的基准。

/**
 * @author maguobin
 * @description: TODO
 */
@SpringBootTest
public class MybatisTest {

    @Autowired
    private StudentService studentService;

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Test
    public void MybatisBatchSaveOneByOne(){


        SqlSession sqlSession = sqlSessionFactory.openSession();

        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start("mybatis plus save one");
            for (int i = 0; i < 100000; i++) {
                Student student = new Student();
                student.setStuid("6840"+i);
                student.setName("zhangsan"+i);

                student.setAge((i%100));


                if(i%2==0){
                    student.setSex(0);
                }else {
                    student.setSex(1);
                }

                student.setDept("计算机学院");
                student.setAddress("广东省广州市番禺"+i+"号");
                //一条一条插入
                studentService.save(student);
            }
            sqlSession.commit();
            stopWatch.stop();
            System.out.println("mybatis plus save one:" + stopWatch.getTotalTimeMillis());

        } finally {
            sqlSession.close();
        }

    }

}

发现花费了195569毫秒

MyBatis批量插入数据优化_mybatis 批量处理_07


  1. mybatis-plus 的 saveBatch 方法

现在尝试 mybatis-plus 提供的 saveBatch 方法,期望它能够提高性能。

@Test
    public void MybatissaveBatch(){


        SqlSession sqlSession = sqlSessionFactory.openSession();

        try {

            List<Student> students = new ArrayList<>();
            StopWatch stopWatch = new StopWatch();

            stopWatch.start("mybatis plus save batch");
            for (int i = 0; i < 100000; i++) {
                Student student = new Student();
                student.setStuid("6840"+i);
                student.setName("zhangsan"+i);

                student.setAge((i%100));


                if(i%2==0){
                    student.setSex(0);
                }else {
                    student.setSex(1);
                }

                student.setDept("计算机学院");
                student.setAddress("广东省广州市番禺"+i+"号");
                //一条一条插入
                students.add(student);
            }

            studentService.saveBatch(students);
            sqlSession.commit();
            stopWatch.stop();
            System.out.println("mybatis plus save batch:" + stopWatch.getTotalTimeMillis());

        } finally {
            sqlSession.close();
        }

    }

发现花费9204毫秒,比一条条插入数据性能提高十几倍

MyBatis批量插入数据优化_mybatis 批量处理_08

  • 3.手动拼接 SQL:挑战传统的方式
<insert id="saveBatch2">
    insert into springboot_mp.tb_student ( stuid, name, age, sex, dept, address)
    values
    <foreach collection="students" item="stu" index="index" separator=",">
          ( #{stu.stuid}, #{stu.name}, #{stu.age}, #{stu.sex}, #{stu.dept}, #{stu.address})
    </foreach>

</insert>

发现花费10958毫秒,比一条条插入数据性能提高十几倍,但是和saveBatch性能相差不大

MyBatis批量插入数据优化_mybatis 批量处理_09

既然都验证都这了,我就在想,要不要使用JDBC批量插入进行验证一下,看会不会出现原始的才是最好的结果

  • 4.JDBC 的 executeBatch 方法

尝试直接使用 JDBC 提供的 executeBatch 方法,看是否有意外的性能提升。

@Test
    public void JDBCSaveBatch() throws SQLException {


        SqlSession sqlSession = sqlSessionFactory.openSession();
        Connection connection = sqlSession.getConnection();
        connection.setAutoCommit(false);



        String sql ="insert into springboot_mp.tb_student ( stuid, name, age, sex, dept, address) values (?, ?, ?, ?, ?, ?);";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {

            List<Student> students = new ArrayList<>();
            StopWatch stopWatch = new StopWatch();
            stopWatch.start("mybatis plus JDBCSaveBatch");
            for (int i = 0; i < 100000; i++) {
                statement.setString(1,"6840"+i);
                statement.setString(2,"zhangsan"+i);
                statement.setInt(3,(i%100));
                if(i%2==0){
                    statement.setInt(4,0);
                }else {
                    statement.setInt(4,1);
                }
                statement.setString(5,"计算机学院");
                statement.setString(6,"广东省广州市番禺"+i+"号");


                statement.addBatch();
            }

            statement.executeBatch();
            connection.commit();
            stopWatch.stop();
            System.out.println("mybatis plus JDBCSaveBatch:" + stopWatch.getTotalTimeMillis());

        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }
        finally {

            sqlSession.close();
        }

JDBC executeBatch 的性能会好点,耗费6667毫秒

MyBatis批量插入数据优化_mybatis 批量处理_10

但是感觉到这里以后,觉得时候还是比较长,有没有可以再进行优化的方式,然后我就在ClientPreparedStatement类中发现有一个叫做rewriteBatchedStatements 的属性,从名字来看是要重写批操作的 Statement,前面batchHasPlainStatements 已经是 false,取反肯定是 true,所以只要这参数是 true 就会进行一波操作。rewriteBatchedStatements默认是 false。

MyBatis批量插入数据优化_mybatis 批量处理_11

MyBatis批量插入数据优化_mybatis 批量处理_12

MyBatis批量插入数据优化_mybatis 批量处理_13

大家也可以自行网上搜索一下这个神奇的属性然后我在url添加上这个属性

MyBatis批量插入数据优化_mybatis 批量处理_14

然后继续跑了下 mybatis-plus 自带的 saveBatch,果然性能大大提高直接由原来的9204毫秒,提升到现在的3903毫秒

再来跑一下JDBC 的 executeBatch ,果然也提高了。

直接由原来的6667毫秒,提升到了3794毫秒

MyBatis批量插入数据优化_mybatis 批量处理_15

结果对比

批量保存方式

数据量(条)

耗时(ms)

单条循环插入

100000

195569

mybatis-plus saveBatch

100000

9204

mybatis-plus saveBatch(添加 rewrite 参数)

100000

3903

手动拼接 SQL

100000

6667

JDBC executeBatch

100000

10958

JDBC executeBatch(添加 rewrite 参数)

100000

3794

结论

通过实验结果,我们可以得出以下结论:

  • mybatis-plus 的 saveBatch 方法相比单条循环插入在性能上有所提升,但仍然不够理想。
  • JDBC 的 executeBatch 方法在默认情况下性能与 mybatis-plus 的 saveBatch 类似,但通过设置 rewriteBatchedStatements 参数为 true 可显著提高性能。
  • rewriteBatchedStatements 参数的作用是将一批插入拼接成 insert into xxx values (a),(b),(c)... 这样的一条语句形式,提高了性能。

优化建议

如果您在项目中需要进行批量插入操作,我建议考虑以下优化方案:

  • 如果使用 mybatis-plus,可以尝试将 JDBC 连接字符串中的 rewriteBatchedStatements 参数设置为 true,以提高 saveBatch 方法的性能。


标签:statement,批量,mybatis,插入,plus,student,MyBatis,stopWatch
From: https://blog.51cto.com/maguobin/9126700

相关文章

  • 【技术探讨】用户使用其他厂家433MHz无线模块时,购买样品OK,小批量100个就会出现偶尔无
    许多用户使用其他厂家的433M透传无线模块反馈这样的问题:前期购买几个样品测试,在无线信号覆盖半径内,收发包测试都很稳定,但是小批量购买100个模块收发就会出现无法收发的情况。这是什么原因呢?首先科普一下,无线电波,在同一个信道同一时刻只允许一个节点发射行为。433M的无线透传模块,没......
  • JdbcTemplate的基本使用-批量新增
    JdbcTemplate的基本配置参考我的上一篇文章:JdbcTemplate的基本使用-新增批量增加可以使用jdbcTemplate.batchUpdate()方法,示例如下:UserServiceImpl增加批量增加方法:packageservice;importdao.UserDao;importentity.User;importorg.springframework.beans.factory.annotat......
  • 批量同步 开源某一个公司或者个人 大量开源项目的小工具(避免手工一个一个的同步)
    fromrequests.authimportHTTPBasicAuthimportrequestsimportosimportjsonrepos=set()defdownload_repo(output,name,href):print(f'Cloning{name}{href}')os.system(f'gitclone--depth1{href}"{output}/{name}"......
  • AI小蜜批量写作助手:多级指令,插件,GPTs满足不同写作需求
    为什么会开发这个脚本?爆文项目的核心是矩阵怼量具体怎么做这里介绍很清楚了:AI爆文撸流量主保姆级教程3.0+脚本写作教程(解放双手)我在刚做爆文项目时候,都是手动操作,复制指令,组合指令,粘贴,AI生成内容,然后发布。整个过程流程简单,全部重复劳动。但凡没点耐心,很容易就放弃了。重复......
  • JDBC 批量操作 in 的使用
     我们经常会有这种业务需求,根据一个条件集合去查询一张表的数据,比如:select*fromall_elementtwheret.task_idin(List<taskids>);在java语言中,我们需要用到JDBC来和数据库打交道,那么在JDBC中该如何处理这种需求呢?我们可以有如下几种处理方式方案一:写一个函数把参数集合......
  • SpringCloud微服务实战——搭建企业级开发框架(三十一):自定义MybatisPlus代码生成器实现
      理想的情况下,代码生成可以节省很多重复且没有技术含量的工作量,并且代码生成可以按照统一的代码规范和格式来生成代码,给日常的代码开发提供很大的帮助。但是,代码生成也有其局限性,当牵涉到复杂的业务逻辑时,简单的代码生成功能无法解决。  目前市面上的代码生成器层出不穷,大多......
  • 程序员的30大Mybatis面试问题及答案
    文章目录1.什么是Mybatis?2.Hibernate优点?3.Hibernate缺点?4.Mybatis优点?5.Mybatis缺点?6.什么时候用Mybatis?7.Mybatis的核心组件有哪些?分别是?8.#{}和${}的区别是什么?9.Mybatis中9个动态标签是?10.xml映射文件中,有哪些标签?11.Mybatis支持注解吗?优点?缺点?12.Mybatis动态sql?13.**Mybat......
  • html5实现文件批量上传组件
    一、概述在html5中,相对于之前添加了不少新的元素和属性,在javascript中也添加了一些新的API,这些给我们的开发带来了很多便利。但由于各浏览器的发展步骤不一致,也导致了不同浏览器对html5支持的差异性。 二、实现原理1.在该html5实现的文件批量上传组件中,我们主要是利用html5中的一......
  • 如何批量发送邮件分别给不同的人?
    在现代社会中,电子邮件已经成为人们日常沟通和工作中不可或缺的一部分。对于需要向多个人发送个性化邮件的情况,学会如何批量发送邮件分别给不同的人是非常重要的技能。本文将为您详细介绍这一过程,并提供一些建议,让您能够高效而专业地处理大量邮件收件人。1.选择合适的邮件客户端首......
  • 如何批量发送邮件分别给不同的人?
    在现代社会中,电子邮件已经成为人们日常沟通和工作中不可或缺的一部分。对于需要向多个人发送个性化邮件的情况,学会如何批量发送邮件分别给不同的人是非常重要的技能。本文将为您详细介绍这一过程,并提供一些建议,让您能够高效而专业地处理大量邮件收件人。1.选择合适的邮件客户端首......