mybatis plus 多种批处理方式对比测试

19 篇文章 0 订阅

写了好多年了,求波点赞,收藏,关注,一键三连!!

个人博客:http://59.110.230.15欢迎捧场!

 

最近有个需求,一张主表4张子表,主表添加数据后,4张子表分表要批量新增多条记录。比如:Person insert一条记录;childOne,childTwo,childThree,childFour4张表分别添加10条记录。由此引发的批量操作的几种方式的性能测试。

搭建一个测试spring boot2.4.4项目

mybatis依赖

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.20</version>
        </dependency>

配置集成druid数据源,初始和最大连接配置为50

模拟表如下:

主表

@Data
@TableName("person")
public class Person {

    @QuerySqlField(index = true)
    @TableId(type = IdType.INPUT)
    private long id;

    @QuerySqlField
    @TableField
    private String name;

}

子表:

@Data
@TableName("childone")
public class ChildOne {

    @TableId
    private int id;
    @TableField
    private int parentId;
    @TableField
    private String json;

}

字段完全一致,只是名字为ChildTwo。。。以此来推。使用json字段保存一个长json数据用来模拟稍大一些的数据。

然后分别进行5种方案测试,每种方案测3组(1主表+10*4子表;1主表+100*4子表;1主表+500*4子表;)取avg

方案1 一次发送多个表的insert语句,并且每个子表的insert语句采用单条insert+多values的模式

<insert id="insertAll" parameterType="com.wm.demo1.entity.PersonAll">
		insert into person(id,name) values (#{person.id},#{person.name});
		insert into childone(id,parent_id,json) values
		<foreach collection="listOne" item="child" open="" separator="," close="">
			(#{child.id},#{child.parentId},#{child.json})
		</foreach>
		;
		insert into childtwo(id,parent_id,json) values
		<foreach collection="listTwo" item="child" open="" separator="," close="">
			(#{child.id},#{child.parentId},#{child.json})
		</foreach>
		;
		insert into childThree(id,parent_id,json) values
		<foreach collection="listThree" item="child" open="" separator="," close="">
			(#{child.id},#{child.parentId},#{child.json})
		</foreach>
		;
		insert into childFour(id,parent_id,json) values
		<foreach collection="listFour" item="child" open="" separator="," close="">
			(#{child.id},#{child.parentId},#{child.json})
		</foreach>
		;
	</insert>

方案2 一次发送多个表的insert语句,每个字标的insert语句采用foreach整个insert拼接模式

<insert id="insertAll2" parameterType="com.wm.demo1.entity.PersonAll">
		insert into person(id,name) values (#{person.id},#{person.name});
		<foreach collection="listOne" item="child" open="" separator=";" close="">
			insert into childone(id,parent_id,json) values
				(#{child.id},#{child.parentId},#{child.json})
		</foreach>
		;
		<foreach collection="listTwo" item="child" open="" separator=";" close="">
			insert into childtwo(id,parent_id,json) values
				(#{child.id},#{child.parentId},#{child.json})
		</foreach>
		;
		<foreach collection="listThree" item="child" open="" separator=";" close="">
			insert into childThree(id,parent_id,json) values
				(#{child.id},#{child.parentId},#{child.json})
		</foreach>
		;
		<foreach collection="listFour" item="child" open="" separator=";" close="">
			insert into childFour(id,parent_id,json) values
				(#{child.id},#{child.parentId},#{child.json})
		</foreach>
		;
	</insert>

方案3  mybatis plus saveBatch模式

        long start = System.currentTimeMillis();
        transactionTemplate.execute(status -> {
            long start1 = System.currentTimeMillis();
            personMapper.insert(person);
            log.info("testMultiInsert,主表:{}", System.currentTimeMillis() - start1);
            long start2 = System.currentTimeMillis();
            childOneService.saveBatch(listOne,listOne.size());
            log.info("testMultiInsert,listOne:{}", System.currentTimeMillis() - start2);
            long start3 = System.currentTimeMillis();
            childTwoService.saveBatch(listTwo,listTwo.size());
            log.info("testMultiInsert,listTwo:{}", System.currentTimeMillis() - start3);
            long start4 = System.currentTimeMillis();
            childThreeService.saveBatch(listThree,listThree.size());
            log.info("testMultiInsert,listThree:{}", System.currentTimeMillis() - start4);
            long start5 = System.currentTimeMillis();
            childFourService.saveBatch(listFour,listFour.size());
            log.info("testMultiInsert,listFour:{}", System.currentTimeMillis() - start5);
            return true;
        });
        log.info("使用mybatis plus 的saveBatch方式,time:{}", System.currentTimeMillis() - start);

方案4 采用java代码foreach遍历执行mapper的insert方法

long start = System.currentTimeMillis();
        transactionTemplate.execute(status -> {
            long start1 = System.currentTimeMillis();
            personMapper.insert(person);
            log.info("testMultiInsert2,主表:{}", System.currentTimeMillis() - start1);
            long start2 = System.currentTimeMillis();
            for (ChildOne child : listOne) {
                childOneMapper.insert(child);
            }
            log.info("testMultiInsert2,listOne:{}", System.currentTimeMillis() - start2);
            long start3 = System.currentTimeMillis();
            for (ChildTwo child : listTwo) {
                childTwoMapper.insert(child);
            }
            log.info("testMultiInsert2,listTwo:{}", System.currentTimeMillis() - start3);
            long start4 = System.currentTimeMillis();
            for (ChildThree child : listThree) {
                childThreeMapper.insert(child);
            }
            log.info("testMultiInsert2,listThree:{}", System.currentTimeMillis() - start4);
            long start5 = System.currentTimeMillis();
            for (ChildFour child : listFour) {
                childFourMapper.insert(child);
            }
            log.info("testMultiInsert2,listFour:{}", System.currentTimeMillis() - start5);

方案5 采用一个表发送一次,sql采用单insert拼接多个多value模式

long start = System.currentTimeMillis();
        transactionTemplate.execute(status -> {
            long start1 = System.currentTimeMillis();
            personMapper.insert(person);
            log.info("testMultiInsert3,主表:{}", System.currentTimeMillis() - start1);
            long start2 = System.currentTimeMillis();
            childOneMapper.insertAll(listOne);
            log.info("testMultiInsert3,listOne:{}", System.currentTimeMillis() - start2);
            long start3 = System.currentTimeMillis();
            childTwoMapper.insertAll(listTwo);
            log.info("testMultiInsert3,listTwo:{}", System.currentTimeMillis() - start3);
            long start4 = System.currentTimeMillis();
            childThreeMapper.insertAll(listThree);
            log.info("testMultiInsert3,listThree:{}", System.currentTimeMillis() - start4);
            long start5 = System.currentTimeMillis();
            childFourMapper.insertAll(listFour);
            log.info("testMultiInsert3,listFour:{}", System.currentTimeMillis() - start5);
            return true;
        });
        log.info("使用一个子表一次发送,每次发送采用insert 多value方式。time:{}", System.currentTimeMillis() - start);

直接上次测试结果


新增10条记录
一次发送多个表的insert语句,insert使用多value方式。time:130
一次发送多个表的insert语句,insert使用多value方式。time:159
一次发送多个表的insert语句,insert使用多value方式。time:125
avg138

一次发送多个表的insert语句,insert使用多insert语句方式,time:117
一次发送多个表的insert语句,insert使用多insert语句方式,time:130
一次发送多个表的insert语句,insert使用多insert语句方式,time:122
avg123

使用mybatis plus 的saveBatch方式,time:186
使用mybatis plus 的saveBatch方式,time:171
使用mybatis plus 的saveBatch方式,time:172
avg176

使用一个子表一次发送,每次发送采用insert 多value方式。time:141
使用一个子表一次发送,每次发送采用insert 多value方式。time:133
使用一个子表一次发送,每次发送采用insert 多value方式。time:137
avg137

使用foreach循环调用mybatis plus 的单条insert方式,time:163
使用foreach循环调用mybatis plus 的单条insert方式,time:156
使用foreach循环调用mybatis plus 的单条insert方式,time:162
avg160


新增100条记录
一次发送多个表的insert语句,insert使用多value方式。time:229
一次发送多个表的insert语句,insert使用多value方式。time:253
一次发送多个表的insert语句,insert使用多value方式。time:279
avg254

一次发送多个表的insert语句,insert使用多insert语句方式,time:297
一次发送多个表的insert语句,insert使用多insert语句方式,time:268
一次发送多个表的insert语句,insert使用多insert语句方式,time:281
avg282

使用mybatis plus 的saveBatch方式,time:312
使用mybatis plus 的saveBatch方式,time:286
使用mybatis plus 的saveBatch方式,time:281
avg293

使用一个子表一次发送,每次发送采用insert 多value方式。time:235
使用一个子表一次发送,每次发送采用insert 多value方式。time:226
使用一个子表一次发送,每次发送采用insert 多value方式。time:245
avg235

使用foreach循环调用mybatis plus 的单条insert方式,time:500
使用foreach循环调用mybatis plus 的单条insert方式,time:739
使用foreach循环调用mybatis plus 的单条insert方式,time:580
avg606


新增500条记录
一次发送多个表的insert语句,insert使用多value方式。time:554
一次发送多个表的insert语句,insert使用多value方式。time:518
一次发送多个表的insert语句,insert使用多value方式。time:543
avg538

一次发送多个表的insert语句,insert使用多insert语句方式,time:726
一次发送多个表的insert语句,insert使用多insert语句方式,time:682
一次发送多个表的insert语句,insert使用多insert语句方式,time:659
avg 689

使用mybatis plus 的saveBatch方式,time:724
使用mybatis plus 的saveBatch方式,time:534
使用mybatis plus 的saveBatch方式,time:614
avg624

rewriteBatchedStatements=false,使用mybatis plus 的saveBatch方式,time:849
rewriteBatchedStatements=false,使用mybatis plus 的saveBatch方式,time:944
rewriteBatchedStatements=false,使用mybatis plus 的saveBatch方式,time:1074
avg955

使用一个子表一次发送,每次发送采用insert 多value方式。time:604
使用一个子表一次发送,每次发送采用insert 多value方式。time:594
使用一个子表一次发送,每次发送采用insert 多value方式。time:577
avg592

使用foreach循环调用mybatis plus 的单条insert方式,time:1388
使用foreach循环调用mybatis plus 的单条insert方式,time:1467
使用foreach循环调用mybatis plus 的单条insert方式,time:1473
avg1442

根据测试结果得出我的几个结论:

1. 使用代码foreach循环一条条添加,性能极其差。这个应该是毋庸置疑的。数据量大于10就不建议使用了。

2. 发现会有两次saveBatch的测试,主要是验证rewriteBatchedStatements是否起作用,事实证明,使用saveBatch还是应该开启rewriteBatchedStatements=true,不然JDBC底层会无视executeBatch方法。还是一条条insert发送给MySql去执行。不过网上查到资料显示,saveBatch在批次处理数量小于3时,并不会批量传入而是会选择单条单条新增。(来源于网上资料,未验证过)

3. 对比一次发送的两种不同sql拼接方式,使用insert的多value格式,会比多insert性能略好一点。数据量越大性能好的越多,原理是因为使用多value模式可以减少sql解析

4.对比”一个表发送一次,每次发送isnert的多value方式“和”一次性发送全部表的添加sql和多条insert的表的insert语句采用多value方式“,性能差不多。略微有些偏差的地方猜测是多出的几次连接消耗。但是一条Sql insert多个表,开发时会比较困难,出现在一个表的mapper中耦合了其他表的insert操作。而且Mysql存在一个接收数据量大小的限制,当一次发送超过4M,就会报错。我测试的时候发现的就是大于500就会出现这个错,所以对于数量级要求会更小。而一个表发一次,更符合一般的开发规范,而且易于开发,不混乱。

5.暂时没有证据证明,自己写sql的多value模式一定比开启了rewriteBatchedStatements的mybatis plus saveBatch方法性能好。但是saveBatch用的sql魔板是单条新增的模板,理论上发送过去是多条insert语句而不是多value的一条insert语句,我觉得性能会好一些。目前来看性能方案4最差,方案1最好,其他差不多。 所以如果性能要求没有这么极致,我认为可以使用saveBatch快速开发。

6.还有个要注意的是saveBatch不支持oracle,oracle中批量新增,需要自己写sql一次发送多个insert语句。不然会变成一条条发送,降低效率。

这里是完整测试代码demo,mybatis plus 5种批量操作性能测试

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值