来源:blog.csdn.net/a18505947362/article/details/123667215

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

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

二、准备工作测试环境:SpringBoot项目、MyBatis-Plus框架、MySQL8.0.24、JDK13

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

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

代码语言:javascript代码运行次数:0运行复制

org.springframework.boot

spring-boot-starter-web

com.baomidou

mybatis-plus-boot-starter

3.3.1

mysql

mysql-connector-java

org.projectlombok

lombok

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

代码语言:javascript代码运行次数:0运行复制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.Driver3、Entity实体类(测试)

代码语言:javascript代码运行次数:0运行复制/**

* 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)、释放连接和资源关闭等操作上,(如果数据量大的情况下)极其消耗资源,导致时间长。

代码语言:javascript代码运行次数:0运行复制@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语句耗时等情况,但与第一点相比,提高了极大的性能。

代码语言:javascript代码运行次数:0运行复制@GetMapping("/sql")

public void sql(){

ArrayList 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));

}代码语言:javascript代码运行次数:0运行复制// 使用@Insert注解插入:此处为简便,不写Mapper.xml文件

@Insert("")

int insertSplice(@Param("studentList") List 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上消耗性能。

代码语言:javascript代码运行次数:0运行复制@GetMapping("/saveBatch1")

public void saveBatch1(){

ArrayList 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 参数情况

代码语言:javascript代码运行次数:0运行复制# 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省去对资源相关操作的耗能、减少对事务处理的时间等,从而极大程度上提高执行效率。(目前个人觉得最优方案)

代码语言:javascript代码运行次数:0运行复制@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,否则也无法发挥作用。

对于测试方案的设定、对考虑不周、理解和编写错误的地方等情况,请多指出,共同学习!

------

我们创建了一个高质量的技术交流群,与优秀的人在一起,自己也会优秀起来,赶紧点击加群,享受一起成长的快乐。另外,如果你最近想跳槽的话,年前我花了2周时间收集了一波大厂面经,节后准备跳槽的可以点击这里领取!

推荐阅读1024 程序员节:共迎算力新时代,开源新未来!

Git 2.38 发布,引入巨型仓库管理工具"Scalar"

七万奖金,字节Offer,iPhone 14 Pro 等惊喜奖品··································

你好,我是程序猿DD,10年开发老司机、阿里云MVP、腾讯云TVP、出过书创过业、国企4年互联网6年。从普通开发到架构师、再到合伙人。一路过来,给我最深的感受就是一定要不断学习并关注前沿。只要你能坚持下来,多思考、少抱怨、勤动手,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你看好一个事情,一定是坚持了才能看到希望,而不是看到希望才去坚持。相信我,只要坚持下来,你一定比现在更好!如果你还没什么方向,可以先关注我,这里会经常分享一些前沿资讯,帮你积累弯道超车的资本。

点击领取2022最新10000T学习资料