首页 > 数据库 >SpringBoot+MybatisPlus+Mysql实现批量插入万级数据多种方式与耗时对比

SpringBoot+MybatisPlus+Mysql实现批量插入万级数据多种方式与耗时对比

时间:2024-02-21 14:55:08浏览次数:44  
标签:MybatisPlus SpringBoot StopWatch int SysStudent 万级 插入 stopWatch new

场景

若依前后端分离版本地搭建开发环境并运行项目的教程:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662

若依前后端分离版如何集成的mybatis以及修改集成mybatisplus实现Mybatis增强:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/136203040

基于以上基础,测试批量将万级以上数据插入到mysql数据中的多种方式。

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

实现

1、数据准备

参考上面集成mp时测试用的SysStudent表以及相关代码,每种方式执行前首先将数据库中

表清空。

application.yml中连接mysql的url中添加开启批处理模式的配置

&rewriteBatchedStatements=true

 

2、方式一:最基本的for循环批量插入的方式

直接使用mapper自带的insert方法使用for循环插入数据

编写单元测试

    @Test
    public void foreachInsertData() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < 50000; i++) {
            SysStudent sysStudent = SysStudent.builder().studentName("test").studentAge(i).studentHobby("test").build();
            sysStudentMapper.insert(sysStudent);
        }
        stopWatch.stop();
        System.out.println(stopWatch.shortSummary());
    }

运行结果

时间较长,高达179秒,不推荐使用。

利用for循环进行单条插入时,每次都是在获取连接(Connection)、释放连接和资源关闭等操作上,

(如果数据量大的情况下)极其消耗资源,导致时间长。

当然所有测试时间均是在单元测试中进行,运行时间受多方面影响,不代表最终业务层运行实际时间,

仅用作同等条件方式下耗时对比。

 

3、方式二:使用拼接sql方式实现批量插入数据

在mapper中新增方法

public interface SysStudentMapper extends BaseMapper<SysStudent>
{
    @Insert("<script>" +
            "insert into sys_student (student_name, student_age, student_hobby) values " +
            "<foreach collection='studentList' item='item' separator=','> " +
            "(#{item.studentName}, #{item.studentAge},#{item.studentHobby}) " +
            "</foreach> " +
            "</script>")
    int insertSplice(@Param("studentList") List<SysStudent> studentList);
}

编写单元测试

    @Test
    public void spliceSqlInsertData() {
        ArrayList<SysStudent> students = new ArrayList<>();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < 50000; i++) {
            SysStudent sysStudent = SysStudent.builder().studentName("test").studentAge(i).studentHobby("test").build();
            students.add(sysStudent);
        }
        sysStudentMapper.insertSplice(students);
        stopWatch.stop();
        System.out.println(stopWatch.shortSummary());
    }

运行结果

拼接结果就是将所有的数据集成在一条SQL语句的value值上,其由于提交到服务器上的insert语句少了,网络负载少了,

性能也就提上去。但是当数据量上去后,可能会出现内存溢出、解析SQL语句耗时等情况。

 

4、方式三:使用mybatisplus的saveBatch实现批量插入

使用MyBatis-Plus实现IService接口中批处理saveBatch()方法

编写单元测试

    @Test
    public void batchInsertData() {
        ArrayList<SysStudent> students = new ArrayList<>();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < 50000; i++) {
            SysStudent sysStudent = SysStudent.builder().studentName("test").studentAge(i).studentHobby("test").build();
            students.add(sysStudent);
        }
        iSysStudentService.saveBatch(students,1000);
        stopWatch.stop();
        System.out.println(stopWatch.shortSummary());
    }

运行结果

 

5、方式四:共用SqlSession,关闭自动提交事务实现for循环批量插入大数据量数据

由于同一个SqlSession省去对资源相关操作的耗能、减少对事务处理的时间等,从而极大程度上提高执行效率。

编写单元测试

    @Test
    public void forBatchInsertData() {
        //开启批处理处理模式 BATCH,关闭自动提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH,false);
        //反射获取 Mapper
        SysStudentMapper sysStudentMapper = sqlSession.getMapper(SysStudentMapper.class);
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < 50000; i++) {
            SysStudent sysStudent = SysStudent.builder().studentName("test").studentAge(i).studentHobby("test").build();
            sysStudentMapper.insert(sysStudent);
        }
        //一次性提交事务
        sqlSession.commit();
        //关闭资源
        sqlSession.close();
        stopWatch.stop();
        System.out.println(stopWatch.shortSummary());
    }

引入依赖

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

运行结果

推荐使用

 

6、方式五:使用ThreadPoolTaskExecuror线程池实现批量插入大数据量数据到mysql

将要插入的数据列表按照指定的批次大小分割成多个子列表,并开启多个线程来执行插入操作。

通过 TransactionManager 获取事务管理器,并使用 TransactionDefinition 定义事务属性。

在每个线程中,我们通过 transactionManager.getTransaction() 方法获取事务状态,并在插入操作中使用该状态来管理事务。

在插入操作完成后,根据操作结果调用transactionManager.commit()或 transactionManager.rollback() 方法来提交或回滚事务。

在每个线程执行完毕后,都会调用 CountDownLatch 的 countDown() 方法,以便主线程等待所有线程都执行完毕后再返回。

Java中使用CountDownLatch实现并发流程控制:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126255413

SpringBoot中使用Spring自带线程池ThreadPoolTaskExecutor与Java8CompletableFuture实现异步任务示例:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/136036821

编写单元测试:

    @Test
    public void threadPoolInsertData() {
        ArrayList<SysStudent> students = new ArrayList<>();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < 50000; i++) {
            SysStudent sysStudent = SysStudent.builder().studentName("test").studentAge(i).studentHobby("test").build();
            students.add(sysStudent);
        }
        int count = students.size();
        int pageSize = 1000; //每批次插入的数据量
        int threadNum = count%pageSize == 0?(count/pageSize):(count/pageSize+1); //线程数
        CountDownLatch countDownLatch = new CountDownLatch(threadNum);
        for (int i = 0; i < threadNum; i++) {
            int startIndex = i * pageSize;
            int endIndex = Math.min(count,(i+1)*pageSize);
            List<SysStudent> subList = students.subList(startIndex,endIndex);
            threadPoolTaskExecutor.execute(()->{
                DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
                TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
                try{
                    sysStudentMapper.insertSplice(subList);
                    transactionManager.commit(status);
                }catch (Exception exception){
                    transactionManager.rollback(status);
                    throw exception;
                }finally {
                    countDownLatch.countDown();
                }
            });
        }
        try{
            countDownLatch.await();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        stopWatch.stop();
        System.out.println(stopWatch.shortSummary());
    }

需要引入依赖

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private PlatformTransactionManager transactionManager;

运行结果

推荐使用

 


      

标签:MybatisPlus,SpringBoot,StopWatch,int,SysStudent,万级,插入,stopWatch,new
From: https://www.cnblogs.com/badaoliumangqizhi/p/18025200

相关文章

  • SpringBoot使用git-commit-id-maven-plugin打包
    简介git-commit-id-maven-plugin是一个maven插件,用来在打包的时候将git-commit信息打进jar中。这样做的好处是可以将发布的某版本和对应的代码关联起来,方便查阅和线上项目的维护。至于它的作用,用官方说法,这个功能对于大型分布式项目来说是无价的。功能你是否经常遇到这样的......
  • 华为二面:SpringBoot如何自定义Starter?
    SpringBoot的自动配置机制为开发人员提供了一种轻松集成和配置各种功能的便捷方式。然而,随着项目的复杂性增加,更好地组织和分享通用功能变得至关重要。自定义Starter成为了理想的解决方案,旨在简化项目的依赖管理和自动配置,使开发者能够迅速而灵活地集成特定的功能模块。本文将深......
  • 若依前后端分离版如何集成的mybatis以及修改集成mybatisplus实现Mybatis增强
    场景若依前后端分离版手把手教你本地搭建环境并运行项目:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662SpringBoot中使用PageHelper插件实现Mybatis分页:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/136189442在上面搭建若依前后端分......
  • SpringBoot应用“No primary or single unique constructor found for interface java
    Bug复现今天在写SpringBoot应用时,写一个后端生成图形验证码的业务时,需要用到session保存后端生成的图形验证码的Base64编码,然后前端通过session来显示图形验证码。代码里用到了servlet里的HttpSession类,于是idea自动导入了javax.servlet包里的HttpSession。业务代码如下:packag......
  • 美团面试:Kafka如何处理百万级消息队列?
    美团面试:Kafka如何处理百万级消息队列?在今天的大数据时代,处理海量数据已成为各行各业的标配。特别是在消息队列领域,ApacheKafka作为一个分布式流处理平台,因其高吞吐量、可扩展性、容错性以及低延迟的特性而广受欢迎。但当面对真正的百万级甚至更高量级的消息处理时,如何有效地利......
  • SpringBoot中使用PageHelper插件实现Mybatis分页
    场景SpringBoot中整合Mybatis时一般添加的依赖为       <dependency>           <groupId>org.mybatis.spring.boot</groupId>           <artifactId>mybatis-spring-boot-starter</artifactId>           <version>2.2.1</vers......
  • 编程开发 --- springboot参考文档之概述
    该文档来自于官方,本人只是翻译通顺、忽略废话、术语通俗解释、代码注释等优化工作,目的只是让我们更快的理解它。https://github.com/spring-projects/spring-boot/wiki   第一章法律版权声明  spring.io ©2012-2024您可以复制本文件供自己使用或分发给他人,但不得......
  • 基于Java+SpringBoot+vue的采购管理系统(源码及功能分析)
    前言:随着全球化和信息化的发展,企业采购管理面临越来越多的挑战。传统的采购方式往往涉及到多个繁琐的步骤,包括供应商筛选、询价、招投标等,这些过程不仅耗时,而且容易出错。为了解决这些问题,供应商、询价、招投标一体化系统应运而生。该系统通过集成供应商管理、询价管理、招投标......
  • springboot整合activiti工作流(源码及功能分析)
    前言activiti工作流引擎项目,企业erp、oa、hr、crm等企事业办公系统轻松落地,一套完整并且实际运用在多套项目中的案例,满足日常业务流程审批需求。一、项目形式springboot+vue+activiti集成了activiti在线编辑器,流行的前后端分离部署开发模式,快速开发平台,可插拔工作流服务。工作......
  • SpringBoot 实现热插拔AOP,非常实用!
    现在有这么一个需求:就是我们日志的开与关是交给使用人员来控制的,而不是由我们开发人员固定写死的。大家都知道可以用aop来实现日志管理,但是如何动态的来实现日志管理呢?aop源码中的实现逻辑中有这么一个步骤,就是会依次扫描Advice的实现类,然后执行。我们要做的就是自定义一个advice......