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

场景

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

若依前后端分离版手把手教你本地搭建环境并运行项目_本地运行若依前后端分离-CSDN博客

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

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

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

注:

博客:
霸道流氓气质-CSDN博客

实现

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实现并发流程控制:

Java中使用CountDownLatch实现并发流程控制_countdownlatch设置为几-CSDN博客

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

SpringBoot中使用Spring自带线程池ThreadPoolTaskExecutor与Java8CompletableFuture实现异步任务示例_spring boot taskexecutor-CSDN博客

编写单元测试:

    @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;

运行结果

推荐使用



      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/400241.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一命通关动态规划dp

前言 这篇文章详细概括了动态规划的所有题型&#xff0c;以及各个题型的解决公式方法。如果看完这篇动态规划还是不会做题&#xff0c;我来给你补&#xff0c;我爱说点实话。 动态规划 何为动态规划&#xff1f; 动态规划&#xff0c;有一点暴力求解的感觉。用最通俗的语言来…

Shell基础和变量使用

一、Shell概述 1、什么是shell Shell是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问操作系统内核的服务&#xff0c;在用户和内核之间充当翻译官的角色&#xff0c;是一个命令解释器。 Shell是一种编程语言&#xff0c;只是比较古…

【C++游戏开发-03】贪吃蛇

文章目录 前言一、工具准备1.1游戏开发框架1.2visual studio2022下载1.3easyX下载1.4图片素材 二、逻辑分析2.1数据结构2.2蛇的移动2.3吃食物2.4游戏失败 三、DEMO代码实现四、完整源代码总结 &#x1f431;‍&#x1f680;个人博客https://blog.csdn.net/qq_51000584?typeblo…

【hoare基础版】快速排序算法(1)

目录 交换排序 QuickSort快速排序 Hoare整体思路 图解分析 ​ Hoare版本代码 总代码 时间复杂度 交换排序 基本思想&#xff1a;所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换排序的特点是&#xff1a;将键…

论文阅读:How Do Neural Networks See Depth in Single Images?

是由Technische Universiteit Delft(代尔夫特理工大学)发表于ICCV,2019。这篇文章的研究内容很有趣,没有关注如何提升深度网络的性能&#xff0c;而是关注单目深度估计的工作机理。 What they find&#xff1f; 所有的网络都忽略了物体的实际大小&#xff0c;而关注他们的垂直…

java调摄像头和人脸比对

我需要做一个功能&#xff0c;就是网站页面调用摄像头截图。现在由于要用java&#xff0c;就得研究用java怎么调用摄像头。顺带玩了一下人脸比对&#xff0c;资料有点少。 效果 采用javacv实现&#xff0c;先加Maven引用&#xff0c;后面把下载的包再独立引用不用Maven了 …

UI自动化测试篇 :webdriver+ant+jenkins自动化测试实践

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

OpenCV笔记2:鼠标事件实现绘制直线、矩阵、曲线

OpenCV 鼠标事件 创建窗口设置窗口大小鼠标事件监听 判断事件更新起始点和终点绘制线显示图片 打开背景图 """ 鼠标事件 down up move """ import cv2 import numpy as npWINNAME DRAWBOARD st_point (-1, -1) end_point (-1, -1)def draw…

【Unity】管道流动模拟Shader

【Unity】管道流动模拟Shader 抽象模拟管道介质流动的效果&#xff0c;使用顶点片元着色器。可以调整管线光泽&#xff0c;颜色&#xff0c;流动方向&#xff0c;透明度&#xff0c;流动体粗细&#xff0c;流动速度和横断面。 实现效果 Demo效果 Demo下载地址 管线光泽调整 …

proteus8.15图文安装教程

proteus8.15版本可以用STM32系列单片机来进行仿真设计&#xff0c;比7.8版本方便多了&#xff0c;有需要的朋友们可以在公众号后台回复 proteus8.15 获取软件包。 1、下载好软件包&#xff0c;解压如下&#xff0c;右键proteus8.15.sp1以管理员身份运行。 2、第一次安装&#x…

【蓝桥杯单片机入门记录】独立按键

目录 一、键盘、微动开关概述 二、按键工作原理 &#xff08;1&#xff09;按键构成 &#xff08;2&#xff09;&#xff08;蓝桥杯开发板&#xff09;独立按键电路图&#xff08;非实际&#xff0c;参考理解&#xff09; &#xff08;3&#xff09;独立按键工作原理 三、…

【J1939】一、概述,协议基础

文章目录 1. 背景2. 要点3. J1939帧细节3.1 协议数据单元(Protocol Data Unit,PDU)3.2 参数组编号(PGN)3.3 可疑参数编号(Suspect Parameter Number,SPN)参考1. 背景 J1939是一种用于商用车辆的通信协议,它定义了一套车辆电子控制单元之间进行数据通信的规范。J1939协议…

OpenAI 发布文生视频模型 Sora,普通人应该怎么做才能利益最大化?

原文链接&#xff1a; OpenAI 发布文生视频模型 Sora&#xff0c;普通人应该怎么做才能利益最大化&#xff1f; 自从 2022 年 11 月 30 日 ChatGPT 发布之后&#xff0c;每次 OpenAI 再发布新功能都跟过年一样&#xff0c;那叫一个热闹。 包括 GPT 4.0&#xff0c;GPT Store&…

滚雪球学Java(65):深入理解Java中的Map接口:实现原理剖析

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

2024年单服务器部署Mongodb三节点副本集自动化部署脚本

该脚本是为了方便自己学习和工作中部署服务器从而节省时间进行编写&#xff0c;目前能正常部署&#xff0c;创建集群&#xff0c;管理员用户&#xff0c;以及连接都没问题&#xff0c;但是没有开启验证&#xff0c;后续找时间补充。 完整的教程请参考一下我写的技术文章。 20…

十六进制数

1.做一个收电费程序&#xff0c;要求输入使用的电的度数&#xff08;整数&#xff09;以及电费单价&#xff08;实数&#xff09;&#xff0c;输出总的用电费用。 2.提示并输入一个小写字母数据&#xff0c;输出其对应的ASCII值&#xff0c;以及该小写字母对应的大写字母。 3.提…

软件测试工程师linux学习之系统层面相关命令总结

1 linux系统重启和关机的命令 重启命令&#xff1a;reboot 关机命令&#xff1a;shutdown 这两个命令一般很少用到&#xff0c;我们了解即可。 2 查看日志信息命令 什么是日志&#xff0c;日志就是一个一个普通的文本文件&#xff0c;文件里面记录的是软件运行过程中的信息…

市场复盘总结 20240221

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 33% 最常用…

搜维尔科技:用于运动科学的 OptiTrack,范围标记、步态捕捉!

OptiTrack 系统提供世界领先的测量精度和简单易用的工作流程&#xff0c;为研究人员和生物力学师的研究提供理想的 3D 跟踪数据。 对所有主要数字测力台、EMG 和模拟设备的本机即插即用支持为研究人员提供了在 Visual3D、MotionMonitor、MATLAB 和其他第三方生物力学软件包中进…

MySQL数据库基础(十二):子查询(三步走)

文章目录 子查询&#xff08;三步走&#xff09; 一、子查询&#xff08;嵌套查询&#xff09;的介绍 二、子查询的使用 三、总结 子查询&#xff08;三步走&#xff09; 一、子查询&#xff08;嵌套查询&#xff09;的介绍 在一个 select 语句中,嵌入了另外一个 select …