1. Mybatis-Plus 的 IService 接口在处理大量数据插入时,逐条插入与一次性批量插入的效率对比
准备工作:
我们先准备一张user表,现在这张表为空表,接下来我会用逐条插入与一次性批量插入两种方式去往这张表中添加10w条数据。
接下来搭建SpirngBoot工程,配和Mybatis-Plus实现了对user表的后端工程结构的搭建
1.1 使用循环逐条加入10w条数据
代码如下:
package com.mp.service;
import com.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class IUserServiceTest {
@Autowired
private IUserService userService;
@Test
public void testInsert() {
long begin = System.currentTimeMillis();
int total = 10_0000;
for (int i = 0; i < total; i++) {
userService.save(buildUser(i));
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - begin) + "毫秒");
}
private User buildUser(int i) {
User user = new User();
user.setUsername("user_" + i);
user.setPassword("password_" + i);
return user;
}
}
运行结果:
耗时197秒,可以看到速度非常慢。使用循环插入10w数据,相当于进行了10w次网络请求,所以非常耗费时间和资源。
1.2 使用批量插入10w条数据
首先我们先清空user表的数据
truncate table user
代码如下:
package com.mp.service;
import com.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
class IUserServiceTest {
@Autowired
private IUserService userService;
@Test
void testSaveBatch() {
// 准备10万条数据
List<User> list = new ArrayList<>(1000);
long begin = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
list.add(buildUser(i));
// 每1000条批量插入一次
if (i % 1000 == 0) {
userService.saveBatch(list);
list.clear();
}
}
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - begin) + "毫秒");
}
private User buildUser(int i) {
User user = new User();
user.setUsername("user_" + i);
user.setPassword("password_" + i);
return user;
}
}
耗时15秒,执行时间快了很多。这样做了以后虽然网络请求的次数下降为100次,但是最终在数据库执行时还是会有多条insert语句,逐条插入数据。SQL类似这样:
INSERT INTO `mp`.`user` (`id`, `username`, `password`)
VALUES (1815321338194673665, 'user_1', 'password_1');
INSERT INTO `mp`.`user` (`id`, `username`, `password`)
VALUES (1815321338224033794, 'user_2', 'password_2');
INSERT INTO `mp`.`user` (`id`, `username`, `password`)
VALUES (1815321338224033795, 'user_3', 'password_3');
而如果想要得到最佳性能,最好是将多条SQL合并为一条,像这样:
INSERT INTO `mp`.`user` (`id`, `username`, `password`)
VALUES (1815321338194673665, 'user_1', 'password_1')
, (1815321338224033794, 'user_2', 'password_2')
, (1815321338224033795, 'user_3', 'password_3')
该怎么做呢?可以利用MySQL的rewriteBatchedStatements参数实现这种效果
1.3 开启rewriteBatchedStatements配置重试批量插入
MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements。顾名思义,就是重写批处理的statement语句。参考文档:
这个参数的默认值是false,我们需要修改连接参数,将其配置为true
修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
接下来我们再次清空user表的数据
truncate table user
然后再次执行批量插入的测试代码,发现这次插入10w条数据只耗时3s
1.4 总结
关于Mybatis-Plus的IService
接口在处理大量数据插入时,逐条插入与批量插入的效率对比。文章通过实验比较了两种插入方式的性能差异。
-
实验准备:准备了一张空的
user
表,并使用SpringBoot结合Mybatis-Plus搭建了后端工程。 -
逐条插入:编写了一个循环,逐条插入10万条数据,并记录了操作的耗时。结果显示,逐条插入耗时197秒,效率较低。
-
批量插入:清空了
user
表,然后使用批量插入的方式插入数据,每1000条数据执行一次插入操作。结果显示,批量插入耗时15秒,效率明显提高。 -
最佳性能:尽管批量插入提高了效率,但在数据库执行时仍然是多条
INSERT
语句。为了获得最佳性能,应将多条SQL合并为一条,这可以通过设置MySQL的rewriteBatchedStatements
参数为true
来实现。 -
开启rewriteBatchedStatements配置:修改了项目的配置文件,添加了
rewriteBatchedStatements=true
参数,并再次执行了批量插入测试。这次插入10万条数据仅耗时3秒,显示出显著的性能提升。