当前位置:首页 > 科技  > 软件

MyBatis批量插入数据优化,那叫一个优雅!

来源: 责编: 时间:2024-01-02 09:30:34 129观看
导读在项目开发中,我们经常需要进行大量数据的批量插入操作。然而,在实际应用中,插入大量数据时性能常常成为一个瓶颈。在我最近的项目中,我发现了一些能够显著提升批量插入性能的方法,并进行了一系列实验来验证它们的有效性。

在项目开发中,我们经常需要进行大量数据的批量插入操作。然而,在实际应用中,插入大量数据时性能常常成为一个瓶颈。在我最近的项目中,我发现了一些能够显著提升批量插入性能的方法,并进行了一系列实验来验证它们的有效性。RlL28资讯网——每日最新资讯28at.com

今日内容介绍,大约花费15分钟RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

背景介绍

我们使用了 mybatis-plus 框架,并采用其中的 saveBatch 方法进行批量数据插入。然而,通过深入研究源码,我发现这个方法并没有如我期望的那样高效RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

这是因为最终在执行的时候还是通过for循环一条条执行insert,然后再一批的进行flush ,默认批的消息为1000RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

为了找到更优秀的解决方案,我展开了一场性能优化的探索之旅。好了我们现在开始探索RlL28资讯网——每日最新资讯28at.com

实验准备

  • 创建一张表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中添加依赖

图片图片RlL28资讯网——每日最新资讯28at.com

<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: 8890spring:  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生成代码

图片图片RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

探索实验

每次都是插入100000条数据RlL28资讯网——每日最新资讯28at.com

注意:因为我的电脑性能比较好,所以才插入这么多数据,大家可以插入1000进行实验对比RlL28资讯网——每日最新资讯28at.com

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

首先,我采用了传统的单条循环插入方法,将每条数据逐一插入数据库,作为性能对比的基准。RlL28资讯网——每日最新资讯28at.com

/** * @author springboot葵花宝典 * @description: TODO */@SpringBootTestpublic 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毫秒RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

  1. mybatis-plus 的 saveBatch 方法

现在尝试 mybatis-plus 提供的 saveBatch 方法,期望它能够提高性能。RlL28资讯网——每日最新资讯28at.com

@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毫秒,比一条条插入数据性能提高十几倍RlL28资讯网——每日最新资讯28at.com

图片RlL28资讯网——每日最新资讯28at.com

3.手动拼接 SQL:挑战传统的方式RlL28资讯网——每日最新资讯28at.com

<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性能相差不大RlL28资讯网——每日最新资讯28at.com

既然都验证都这了,我就在想,要不要使用JDBC批量插入进行验证一下,看会不会出现原始的才是最好的结果RlL28资讯网——每日最新资讯28at.com

4.JDBC 的 executeBatch 方法RlL28资讯网——每日最新资讯28at.com

尝试直接使用 JDBC 提供的 executeBatch 方法,看是否有意外的性能提升。RlL28资讯网——每日最新资讯28at.com

@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毫秒RlL28资讯网——每日最新资讯28at.com

图片RlL28资讯网——每日最新资讯28at.com

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

图片图片RlL28资讯网——每日最新资讯28at.com

图片RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

大家也可以自行网上搜索一下这个神奇的属性然后我在url添加上这个属性RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

然后继续跑了下 mybatis-plus 自带的 saveBatch,果然性能大大提高直接由原来的9204毫秒,提升到现在的3903毫秒RlL28资讯网——每日最新资讯28at.com

图片图片RlL28资讯网——每日最新资讯28at.com

再来跑一下JDBC 的 executeBatch ,果然也提高了。RlL28资讯网——每日最新资讯28at.com

直接由原来的6667毫秒,提升到了3794毫秒RlL28资讯网——每日最新资讯28at.com

图片RlL28资讯网——每日最新资讯28at.com

结果对比

批量保存方式
RlL28资讯网——每日最新资讯28at.com

数据量(条)
RlL28资讯网——每日最新资讯28at.com

耗时(ms)
RlL28资讯网——每日最新资讯28at.com

单条循环插入
RlL28资讯网——每日最新资讯28at.com

100000
RlL28资讯网——每日最新资讯28at.com

195569
RlL28资讯网——每日最新资讯28at.com

mybatis-plus saveBatch
RlL28资讯网——每日最新资讯28at.com


RlL28资讯网——每日最新资讯28at.com

100000
RlL28资讯网——每日最新资讯28at.com

9204
RlL28资讯网——每日最新资讯28at.com

mybatis-plus saveBatch(添加 rewrite 参数)
RlL28资讯网——每日最新资讯28at.com

100000
RlL28资讯网——每日最新资讯28at.com

3903
RlL28资讯网——每日最新资讯28at.com

手动拼接 SQL
RlL28资讯网——每日最新资讯28at.com

100000
RlL28资讯网——每日最新资讯28at.com

6667
RlL28资讯网——每日最新资讯28at.com

JDBC executeBatch
RlL28资讯网——每日最新资讯28at.com

100000
RlL28资讯网——每日最新资讯28at.com

10958
RlL28资讯网——每日最新资讯28at.com

JDBC executeBatch(添加 rewrite 参数)
RlL28资讯网——每日最新资讯28at.com

100000
RlL28资讯网——每日最新资讯28at.com

3794
RlL28资讯网——每日最新资讯28at.com

结论

通过实验结果,我们可以得出以下结论:RlL28资讯网——每日最新资讯28at.com

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

优化建议

如果您在项目中需要进行批量插入操作,我建议考虑以下优化方案:RlL28资讯网——每日最新资讯28at.com

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

本文链接:http://www.28at.com/showinfo-26-55101-0.htmlMyBatis批量插入数据优化,那叫一个优雅!

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Vue 新一代开发者工具正式开源!

下一篇: Go语言中的性能考虑和优化

标签:
  • 热门焦点
  • 之家push系统迭代之路

    之家push系统迭代之路

    前言在这个信息爆炸的互联网时代,能够及时准确获取信息是当今社会要解决的关键问题之一。随着之家用户体量和内容规模的不断增大,传统的靠"主动拉"获取信息的方式已不能满足用
  • 深度探索 Elasticsearch 8.X:function_score 参数解读与实战案例分析

    深度探索 Elasticsearch 8.X:function_score 参数解读与实战案例分析

    在 Elasticsearch 中,function_score 可以让我们在查询的同时对搜索结果进行自定义评分。function_score 提供了一系列的参数和函数让我们可以根据需求灵活地进行设置。近期
  • 一个注解实现接口幂等,这样才优雅!

    一个注解实现接口幂等,这样才优雅!

    场景码猿慢病云管理系统中其实高并发的场景不是很多,没有必要每个接口都去考虑并发高的场景,比如添加住院患者的这个接口,具体的业务代码就不贴了,业务伪代码如下:图片上述代码有
  • 一文搞定Java NIO,以及各种奇葩流

    一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 梁柱接棒两年,腾讯音乐闯出新路子

    梁柱接棒两年,腾讯音乐闯出新路子

    文丨田静 出品丨牛刀财经(niudaocaijing)7月5日,企鹅FM发布官方公告称由于业务调整,将于9月6日正式停止运营,这意味着腾讯音乐长音频业务走向消亡。腾讯在长音频领域还在摸索。为
  • 东方甄选单飞:有些鸟注定是关不住的

    东方甄选单飞:有些鸟注定是关不住的

    作者:彭宽鸿来源:华尔街科技眼&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;&zwj;东方甄选创始人俞敏洪带队的&ldquo;7天甘肃行&rdquo;直播活动已在近日顺利收官。成立后一
  • 认真聊聊东方甄选:如何告别低垂的果实

    认真聊聊东方甄选:如何告别低垂的果实

    来源:山核桃作者:财经无忌爆火一年后,俞敏洪和他的东方甄选依旧是颇受外界关心的&ldquo;网红&rdquo;。7月5日至9日,为期5天的东方甄选&ldquo;甘肃行&rdquo;首次在自有App内直播,
  • 8月见!小米MIX Fold 3获得3C认证:支持67W快充

    8月见!小米MIX Fold 3获得3C认证:支持67W快充

    这段时间以来,包括三星、一加、荣耀等等有不少品牌旗下的最新折叠屏旗舰都得到了不少爆料,而小米新一代折叠屏旗舰——小米MIX Fold 3此前也屡屡被传
  • 三星折叠屏手机去年销售近1000万台 今年目标定为1500万

    三星折叠屏手机去年销售近1000万台 今年目标定为1500万

    7月29日消息,三星率先发力可折叠手机市场,在全球市场已经取得了非常亮眼的成绩,接下来会进一步巩固和扩大这一优势。三星在推出Galaxy Z Flip5和Galax
Top