首先我们如果for循环依次遍历插入数据效率是十分底下的,大概耗时5分钟左右。
for循环插入测试代码:
@Test
void testSaveOneByOne() {long b = System.currentTimeMillis();for (int i = 1; i <= 100000; i++) {userService.save(buildUser(i));}long e = System.currentTimeMillis();System.out.println("耗时:" + (e - b));
}
MybatisPlus提供的批量新增方法'saveBatch()'十分方便,但效率也没有想象中的高。据测试十万条数据新增大概比for循环提高了10倍,仍需要几十秒。
MP批量新增测试代码:
@Test
void testSaveBatch() {// 准备10万条数据List<User> list = new ArrayList<>(1000);long b = System.currentTimeMillis();for (int i = 1; i <= 100000; i++) {list.add(buildUser(i));// 每1000条批量插入一次if (i % 1000 == 0) {userService.saveBatch(list);list.clear();}}long e = System.currentTimeMillis();System.out.println("花费时间:" + (e - b));
}
代码中每1000次插入一次,是因为一次数据量只能接收在这个范围。如果说for循环请求了Mysql10万次,那么MP提供的方法可以理解为请求了10w/1k=100次。实际上的性能损失就在于这么多请求的次数上。
那么如果想要得到最佳性能,最好是将多条SQL合并为一条,像这样:
INSERT INTO user ( username, password, phone, info, balance, create_time, update_time )
VALUES
(user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01),
(user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01),
(user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01),
(user_4, 123, 18688190004, "", 2000, 2023-07-01, 2023-07-01);
实现这个效果有两种方式,第一种可以在Mybatis的mapper文件中进行动态sql编写,也就是进行foreach插入数据。
第二种,也是最简单的一种就是添加配置。
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=truedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: root
最后经测试,最优的方案下,10万数据仅需几秒就能插入完毕。