先上结论吧,有空可以自测一下,数据量大时运行一次还时挺耗时的
效率比较
小数据量时6中批量更新效率不太明显,根据项目选择合适的即可,以1万条为准做个效率比较,效率从高到低一次排名如下
replace into
和on duplicate key
效率最高mybatis-plus
有取巧嫌疑,因为是分批批量更新,其他几种都是一次更新- for循环凭借sql和jdbctemplate相近,即使5万条,10万条效率也相近
case when
然而有时候我们只能选择case when,因为replace into
和on duplicate key
公司不一定让用,项目也不一定引入mybatis-plus,数据库url中也不一定有allowmultiqueries=true参数,算是一个兜底方案吧,不管用那种方式大数据量时都需要考虑分批
测试结构
环境信息:mysql-8.0.35-winx64,本地win 10
依次为测试次数-平均耗时-最小耗时-最大耗时,单位为毫秒
数据量 | for | case when | replace into | on duplicate key | mybatis-plus | jdbctemplate |
---|---|---|---|---|---|---|
500 | 100-61-41-1202 | 100-66-57-426 | 100-16-10-282 | 100-15-10-293 | 100-73-52-564 | 100-87-59-1449 |
1000 | 100-131-94-2018 | 100-241-219-675 | 100-28-18-376 | 100-25-17-331 | 100-117-98-599 | 100-188-136-2397 |
5000 | 100-852-735-8297 | 100-11219-10365-13496 | 100-95-83-569 | 100-93-82-552 | 100-618-517-1415 | 100-1161-911-9334 |
10000 | 10-3957-2370-17304 | 10-45537-44465-48119 | 100-191-171-762 | 100-188-169-772 | 100-1309-1085-5021 | 100-3671-2563-31112 |
50000 | 10-50106-34568-130651 | 卡死不动 | 100-1026-919-1868 | 100-1062-945-1934 | 100-8062-6711-20841 | 100-48744-35482-191011 |
100000 | 10-160170-106223-264434 | 卡死不动 | 10-2551-2292-3688 | 10-2503-2173-3579 | 100-17205-14436-24881 | 10-169771-110522-343278 |
心得:
sql语句for循环效率其实相当高的,因为它仅仅有一个循环体,只不过最后update语句比较多,量大了就有可能造成sql阻塞,同时在mysql的url上需要加上allowmultiqueries=true参数,即 jdbc:mysql://localhost:3306/mysqltest?characterencoding=utf-8&allowmultiqueries=true(公司项目不一定加,我们也不一定有权限加)。
case when虽然最后只会有一条更新语句,但是xml中的循环体有点多,每一个case when 都要循环一遍list集合,所以大批量拼sql的时候会比较慢,所以效率问题严重。使用的时候建议分批插入(我们公司一直用的就是这种,但是必须分批)。
duplicate key update可以看出来是最快的,但是公司一般都禁止使用replace into和insert into … on duplicate key update,这种sql有可能会造成数据丢失和主从上表的自增id值不一致。而且用这个更新时,记得一定要加上id,而且values()括号里面放的是数据库字段,不是java对象的属性字段
根据效率,安全方面综合考虑,选择适合的很重要。
数据库
create table `people` ( `id` bigint(8) not null auto_increment, `first_name` varchar(50) not null default '', `last_name` varchar(50) not null default '', primary key (`id`) ) engine=innodb default charset=utf8mb4
初始化测试数据
//初始化10w数据 @test void init10wdata() { for (int i = 0; i < 100000; i++) { people people = new people(); people.setfirstname(uuid.randomuuid().tostring()); people.setlastname(uuid.randomuuid().tostring()); peopledao.insert(people); } }
批量修改方案
第一种 for
<!-- 批量更新第一种方法,通过接收传进来的参数list进行循环组装sql --> <update id="updatebatch" parametertype="java.util.list"> <foreach collection="list" item="item" index="index" open="" close="" separator=";"> update people <set> <if test="item.firstname != null"> first_name = #{item.firstname,jdbctype=varchar}, </if> <if test="item.lastname != null"> last_name = #{item.lastname,jdbctype=varchar}, </if> </set> where id = #{item.id,jdbctype=bigint} </foreach> </update>
第二种 case when
<!-- 批量更新第二种方法,通过 case when语句变相的进行批量更新 --> <update id="updatebatch2" parametertype="java.util.list"> update people <set> <foreach collection="list" item="item"> <if test="item.firstname != null"> first_name = case when id = #{item.id} then #{item.firstname} else first_name end, </if> <if test="item.lastname != null"> last_name = case when id = #{item.id} then #{item.lastname} else last_name end, </if> </foreach> </set> where id in <foreach collection="list" item="item" separator="," open="(" close=")"> #{item.id} </foreach> </update>
第三种 replace into
<!-- 批量更新第三种方法,通过 replace into --> <update id="updatebatch3" parametertype="java.util.list"> replace into people (id,first_name,last_name) values <foreach collection="list" index="index" item="item" separator=","> (#{item.id}, #{item.firstname}, #{item.lastname}) </foreach> </update>
第四种 on duplicate key update
<!-- 批量更新第四种方法,通过 duplicate key update --> <update id="updatebatch4" parametertype="java.util.list"> insert into people (id,first_name,last_name) values <foreach collection="list" index="index" item="item" separator=","> (#{item.id}, #{item.firstname}, #{item.lastname}) </foreach> on duplicate key update id=values(id),first_name=values(first_name),last_name=values(last_name) </update>
第五种mybatis-plus提供的的批量更新
default boolean updatebatchbyid(collection<t> entitylist) { return this.updatebatchbyid(entitylist, 1000); } boolean updatebatchbyid(collection<t> entitylist, int batchsize);
mybatis-plus提供的批量更新是分批批量更新,默认每批1000条,可以指定分批的条数,每批执行完成后提交一下事务,不加@transactional可能会出现第一批更新成功了,第二批更新失败了的情况.
第六种jdbctemplate提供的批量更新
测试代码
/** * peopledao继承基类 */ @mapper @repository public interface peopledao extends mybatisbasedao<people, long> { void updatebatch(@param("list") list<people> list); void updatebatch2(list<people> list); void updatebatch3(list<people> list); void updatebatch4(list<people> list); }
@springboottest class peoplemappertest { @resource peoplemapper peoplemapper; @resource peopleservice peopleservice; @resource jdbctemplate jdbctemplate; @test void init10wdata() { for (int i = 0; i < 100000; i++) { people people = new people(); people.setfirstname(uuid.randomuuid().tostring()); people.setlastname(uuid.randomuuid().tostring()); peoplemapper.insert(people); } } @test void updatebatch() { list<people> list = new arraylist(); int loop = 100; int count = 5000; long maxcost = 0l;//最长耗时 long mincost = long.valueof(integer.max_value);//最短耗时 for (int j = 0; j < count; j++) { people people = new people(); people.setid(threadlocalrandom.current().nextint(0, 100000)); people.setfirstname(uuid.randomuuid().tostring()); people.setlastname(uuid.randomuuid().tostring()); list.add(people); } long starttime = system.currenttimemillis(); for (int i = 0; i < loop; i++) { long curstarttime = system.currenttimemillis(); // peoplemapper.updatebatch4(list); // peopleservice.updatebatchbyid(list); jdbctemplatebatchupdate(list); long curcosttime = system.currenttimemillis() - curstarttime; if (maxcost < curcosttime) { maxcost = curcosttime; } if (mincost > curcosttime) { mincost = curcosttime; } } system.out.println(loop + "-" + (system.currenttimemillis() - starttime) / loop + "-" + mincost + "-" + maxcost ); } private void jdbctemplatebatchupdate (list<people> list){ string sql = "update people set first_name=?,last_name=? where id = ?"; list<object[]> params = list.stream().map(item -> new object[]{item.getfirstname(), item.getlastname(), item.getid()}).collect(collectors.tolist()); jdbctemplate.batchupdate(sql,params); } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。