首页 > 数据库 >MySQL批量插入数据的四种方案(性能测试对比)

MySQL批量插入数据的四种方案(性能测试对比)

时间:2022-12-20 10:58:15浏览次数:38  
标签:student 批量 Student System 插入 测试 MySQL 约等于 四种

本文记录个人使用MySQL插入大数据总结较实用的方案,通过对常用插入大数据的4种方式进行测试,即for循环单条、拼接SQL、批量插入saveBatch()、循环 + 开启批处理模式,得出比较实用的方案心得。

一、前言

最近趁空闲之余,在对MySQL数据库进行插入数据测试,对于如何快速插入数据的操作无从下手,在仅1W数据量的情况下,竟花费接近47s,实在不忍直视!在不断摸索之后,整理出一些较实用的方案。

二、准备工作

测试环境:SpringBoot项目、MyBatis-Plus框架、MySQL8.0.24、JDK13

前提:SpringBoot项目集成MyBatis-Plus上述文章有配置过程,同时实现IService接口用于进行批量插入数据操作saveBatch()方法

1、Maven项目中pom.xml文件引入的相关依赖如下

 <dependencies>
 
  <!-- SpringBoot Web模块依赖 -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
 
  <!-- MyBatis-Plus 依赖 -->
  <dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>3.3.1</version>
  </dependency>
 
  <!-- 数据库连接驱动 -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
  </dependency>
  
  <!-- 使用注解,简化代码-->
  <dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
  </dependency>
  
 </dependencies>

2、application.yml配置属性文件内容(重点:开启批处理模式)

server:
    # 端口号 
    port: 8080
 
#  MySQL连接配置信息(以下仅简单配置,更多设置可自行查看)
spring:
    datasource:
        #  连接地址(解决UTF-8中文乱码问题 + 时区校正)
        #         (rewriteBatchedStatements=true 开启批处理模式)
        url: jdbc:mysql://127.0.0.1:3306/bjpowernode?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
        #  用户名
        username: root
        #  密码
        password: xxx
        #  连接驱动名称
        driver-class-name: com.mysql.cj.jdbc.Driver

3、Entity实体类(测试)

/**
 *   Student 测试实体类
 *   
 *   @Data注解:引入Lombok依赖,可省略Setter、Getter方法
 *   @author LBF
 *   @date 2022/3/18 16:06
 */
@Data
@TableName(value = "student")
public class Student {
    
    /**  主键  type:自增 */
    @TableId(type = IdType.AUTO)
    private int id;
 
    /**  名字 */
    private String name;
 
    /**  年龄 */
    private int age;
 
    /**  地址 */
    private String addr;
 
    /**  地址号  @TableField:与表字段映射 */
    @TableField(value = "addr_num")
    private String addrNum;
 
    public Student(String name, int age, String addr, String addrNum) {
        this.name = name;
        this.age = age;
        this.addr = addr;
        this.addrNum = addrNum;
    }
}

4、数据库student表结构(注意:无索引)

 

 

三、测试工作

简明:完成准备工作后,即对for循环、拼接SQL语句、批量插入saveBatch()、循环插入+开启批处理模式,该4种插入数据的方式进行测试性能。

注意:测试数据量为5W、单次测试完清空数据表(确保不受旧数据影响)

以下测试内容可能受测试配置环境、测试规范和数据量等诸多因素影响,读者可自行结合参考进行测试

1、for循环插入(单条)(总耗时:177秒)

总结:测试平均时间约是177秒,实在是不忍直视(捂脸),因为利用for循环进行单条插入时,每次都是在获取连接(Connection)、释放连接和资源关闭等操作上,(如果数据量大的情况下)极其消耗资源,导致时间长。

@GetMapping("/for")
public void forSingle(){
    // 开始时间
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 50000; i++){
        Student student = new Student("李毅" + i,24,"张家界市" + i,i + "号");
        studentMapper.insert(student);
    }
    // 结束时间
    long endTime = System.currentTimeMillis();
    System.out.println("插入数据消耗时间:" + (endTime - startTime));
}

(1)第一次测试结果:190155 约等于 190秒

(2)第二次测试结果:175926 约等于 176秒(服务未重启)

(3)第三次测试结果:174726 约等于 174秒(服务重启)

2、拼接SQL语句(总耗时:2.9秒)

简明:拼接格式:insert into student(xxxx) value(xxxx),(xxxx),(xxxxx).......

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

但是当数据量上去后,可能会出现内存溢出、解析SQL语句耗时等情况,但与第一点相比,提高了极大的性能。

@GetMapping("/sql")
public void sql(){
    ArrayList<Student> arrayList = new ArrayList<>();
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 50000; i++){
        Student student = new Student("李毅" + i,24,"张家界市" + i,i + "号");
        arrayList.add(student);
    }
    studentMapper.insertSplice(arrayList);
    long endTime = System.currentTimeMillis();
    System.out.println("插入数据消耗时间:" + (endTime - startTime));
}
// 使用@Insert注解插入:此处为简便,不写Mapper.xml文件
@Insert("<script>" +
        "insert into student (name,age,addr,addr_num) values " +
        "<foreach collection='studentList' item='item' separator=','> " +
        "(#{item.name}, #{item.age}, #{item.addr}, #{item.addrNum}) " +
        "</foreach> " +
        "</script>")
int insertSplice(@Param("studentList") List<Student> studentList);

(1)第一次测试结果:3218 约等于 3.2秒

(2)第二次测试结果:2592 约等于 2.6秒(服务未重启)

(3)第三次测试结果:3082 约等于 3.1秒(服务重启)

3、批量插入saveBatch(总耗时:2.7秒)

简明:使用MyBatis-Plus实现IService接口中批处理saveBatch()方法,对底层源码进行查看时,可发现其实是for循环插入,但是与第一点相比,为什么性能上提高了呢?因为利用分片处理(batchSize = 1000) + 分批提交事务的操作,从而提高性能,并非在Connection上消耗性能。

@GetMapping("/saveBatch1")
public void saveBatch1(){
    ArrayList<Student> arrayList = new ArrayList<>();
    long startTime = System.currentTimeMillis();
    // 模拟数据
    for (int i = 0; i < 50000; i++){
        Student student = new Student("李毅" + i,24,"张家界市" + i,i + "号");
        arrayList.add(student);
    }
    // 批量插入
    studentService.saveBatch(arrayList);
    long endTime = System.currentTimeMillis();
    System.out.println("插入数据消耗时间:" + (endTime - startTime));
}

(1)第一次测试结果:2864 约等于 2.9秒

(2)第二次测试结果:2302 约等于 2.3秒(服务未重启)

(3)第三次测试结果:2893 约等于 2.9秒(服务重启)

重点注意:MySQL JDBC驱动默认情况下忽略saveBatch()方法中的executeBatch()语句,将需要批量处理的一组SQL语句进行拆散,执行时一条一条给MySQL数据库,造成实际上是分片插入,即与单条插入方式相比,有提高,但是性能未能得到实质性的提高。

测试:数据库连接URL地址缺少 rewriteBatchedStatements = true 参数情况

#  MySQL连接配置信息
spring:
    datasource:
        #  连接地址(未开启批处理模式)
        url: jdbc:mysql://127.0.0.1:3306/bjpowernode?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
        #  用户名
        username: root
        #  密码
        password: xxx
        #  连接驱动名称
        driver-class-name: com.mysql.cj.jdbc.Driver

测试结果:10541 约等于 10.5秒(未开启批处理模式)

4、循环插入 + 开启批处理模式(总耗时:1.7秒)(重点:一次性提交)

简明:开启批处理,关闭自动提交事务,共用同一个SqlSession之后,for循环单条插入的性能得到实质性的提高;由于同一个SqlSession省去对资源相关操作的耗能、减少对事务处理的时间等,从而极大程度上提高执行效率。(目前个人觉得最优方案)

@GetMapping("/forSaveBatch")
public void forSaveBatch(){
    //  开启批量处理模式 BATCH 、关闭自动提交事务 false
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH,false);
    //  反射获取,获取Mapper
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    long startTime = System.currentTimeMillis();
    for (int i = 0 ; i < 50000 ; i++){
        Student student = new Student("李毅" + i,24,"张家界市" + i,i + "号");
        studentMapper.insertStudent(student);
    }
    // 一次性提交事务
    sqlSession.commit();
    // 关闭资源
    sqlSession.close();
    long endTime = System.currentTimeMillis();
    System.out.println("总耗时: " + (endTime - startTime));
}

(1)第一次测试结果:1831 约等于 1.8秒

(2)第二次测试结果:1382 约等于 1.4秒(服务未重启)

(3)第三次测试结果:1883 约等于 1.9秒(服务重启)

四、总结

本文记录个人学习MySQL插入大数据一些方案心得,可得知主要是在获取连接、关闭连接、释放资源和提交事务等方面较耗能,其中最需要注意是开启批处理模式,即URL地址的参数:rewriteBatchedStatements = true,否则也无法发挥作用。

标签:student,批量,Student,System,插入,测试,MySQL,约等于,四种
From: https://www.cnblogs.com/harda/p/16993718.html

相关文章

  • 【MySQL】登录密码连续错误锁定账户,Failed-Login Tracking 和 Temporary Account Lock
    MySQL8.0.19及更新版本,支持连续几次输入错误密码,锁定账户功能FAILED_LOGIN_ATTEMPTS:代表尝试失败的次数PASSWORD_LOCK_TIME:代表锁定的时间,单位天。#指定策略和加密插件......
  • mysql事务隔离级别详解和实战
    A事务做了操作 没有提交 对B事务来说 就等于没做 获取的都是之前的数据但是 在A事务中查询的话 查到的都是操作之后的数据没有提交的数据只有自己看得到,并没有update......
  • 俗话:MySQL索引
    MySQL凭借着出色的性能、低廉的成本、丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库。虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程......
  • mysql 获取某月所有上午的数据
    (SELECT*,count(1),SUM(if(ARRIVE.ARRIVE_TIME!='无',1,0))assFROMlt_cleanroute_point_arriveASARRIVEWHEREDATE_FORMAT(data_dt,'%Y-%m......
  • 一文速学-玩转MySQL获取时间、格式转换各类操作方法详解
    本文正在参加「技术专题19期漫谈数据库技术」活动前言时间在数据库中经常作为时间索引,在数据入库和出库以及更新的时候都需要变化。在一些指标计算或者是提取某段时间的......
  • MySQL索引背后的数据结构及算法原理
    摘要:看到的一篇关于MySql索引的介绍,感觉比较经典,直接转了。 摘要本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题。特别需要说明的是,MySQL支持诸......
  • GitHub/GitLab 为不同的项目修改提交名字 user.name 和邮箱 user.email(附:批量处理脚本
    背景大疫情的背景下,家里的电脑需要同时支撑自己和公司的项目,根据GitHub/GitLab网站的提交记录上看,其是根据邮箱来辨识用户的,所以有必要分别针对不同的项目设置不同的Gi......
  • 实时采集MySQL数据之轻量工具Maxwell实操
    @目录概述定义原理Binlog说明Maxwell和Canal的区别部署安装MySQL准备初始化Maxwell元数据库Maxwell进程启动命令行参数配置文件实时监控Mysql输出KafkaKafkaTopic分区控制......
  • MySQL
    MySQL导入数据本章节我们为大家介绍几种简单的MySQL导入数据命令。1、mysql命令导入使用mysql命令导入语法格式为:mysql-u用户名-p密码<要导入的数据库数......
  • MySQL 8.0.0 版本发布,亮点都在这了!
    MySQL是一个开放源码的小型关联式数据库管理系统,开发者为瑞典MySQLAB公司。目前MySQL被广泛地应用在Internet上的中小型网站中。由于其体积小、速度快、总体拥有成本低......