SpringBoot中六种批量更新Mysql 方式效率对比

打印 上一主题 下一主题

主题 861|帖子 861|积分 2593

SpringBoot中六种批量更新Mysql 方式效率对比

先上结论吧,有空可以自测一下,数据量大时运行一次还时挺耗时的
效率比较

小数据量时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
依次为测试次数-均匀耗时-最小耗时-最大耗时,单位为毫秒
数据量forcase whenreplace intoON DUPLICATE KEYmybatis-plusJdbcTemplate500100-61-41-1202100-66-57-426100-16-10-282100-15-10-293100-73-52-564100-87-59-14491000100-131-94-2018100-241-219-675100-28-18-376100-25-17-331100-117-98-599100-188-136-23975000100-852-735-8297100-11219-10365-13496100-95-83-569100-93-82-552100-618-517-1415100-1161-911-93341000010-3957-2370-1730410-45537-44465-48119100-191-171-762100-188-169-772100-1309-1085-5021100-3671-2563-311125000010-50106-34568-130651卡死不动100-1026-919-1868100-1062-945-1934100-8062-6711-20841100-48744-35482-19101110000010-160170-106223-264434卡死不动10-2551-2292-368810-2503-2173-3579100-17205-14436-2488110-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对象的属性字段
  • 根据效率,安全方面综合考虑,选择适当的很重要。
数据库

  1. CREATE TABLE `people` (
  2.   `id` bigint(8) NOT NULL AUTO_INCREMENT,
  3.   `first_name` varchar(50) NOT NULL DEFAULT '',
  4.   `last_name` varchar(50) NOT NULL DEFAULT '',
  5.   PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
复制代码
初始化测试数据

  1. //初始化10w数据
  2. @Test
  3. void init10wData() {
  4.     for (int i = 0; i < 100000; i++) {
  5.         People people = new People();
  6.         people.setFirstName(UUID.randomUUID().toString());
  7.         people.setLastName(UUID.randomUUID().toString());
  8.         peopleDAO.insert(people);
  9.     }
  10. }
复制代码
批量修改方案

第一种 for

  1. <!-- 批量更新第一种方法,通过接收传进来的参数list进行循环组装sql -->
  2. <update id="updateBatch" parameterType="java.util.List">
  3.     <foreach collection="list" item="item" index="index" open="" close="" separator=";">
  4.         update people
  5.         <set>
  6.             <if test="item.firstName != null">
  7.                 first_name = #{item.firstName,jdbcType=VARCHAR},
  8.             </if>
  9.             <if test="item.lastName != null">
  10.                 last_name = #{item.lastName,jdbcType=VARCHAR},
  11.             </if>
  12.         </set>
  13.         where id = #{item.id,jdbcType=BIGINT}
  14.     </foreach>
  15. </update>
复制代码
第二种 case when

  1. <!-- 批量更新第二种方法,通过 case when语句变相的进行批量更新 -->
  2. <update id="updateBatch2" parameterType="java.util.List">
  3.     update people
  4.     <set>
  5.         <foreach collection="list" item="item">
  6.             <if test="item.firstName != null">
  7.                 first_name = case when id = #{item.id} then #{item.firstName} else first_name end,
  8.             </if>
  9.             <if test="item.lastName != null">
  10.                 last_name = case when id = #{item.id} then #{item.lastName} else last_name end,
  11.             </if>
  12.         </foreach>
  13.     </set>
  14.     where id in
  15.     <foreach collection="list" item="item" separator="," open="(" close=")">
  16.         #{item.id}
  17.     </foreach>
  18. </update>
复制代码
第三种 replace into

  1. <!-- 批量更新第三种方法,通过 replace into  -->
  2. <update id="updateBatch3" parameterType="java.util.List">
  3.     replace into people
  4.     (id,first_name,last_name) values
  5.     <foreach collection="list" index="index" item="item" separator=",">
  6.         (#{item.id},
  7.         #{item.firstName},
  8.         #{item.lastName})
  9.     </foreach>
  10. </update>
复制代码
第四种 ON DUPLICATE KEY UPDATE

  1. <!-- 批量更新第四种方法,通过 duplicate key update  -->
  2. <update id="updateBatch4" parameterType="java.util.List">
  3.     insert into people
  4.     (id,first_name,last_name) values
  5.     <foreach collection="list" index="index" item="item" separator=",">
  6.         (#{item.id},
  7.         #{item.firstName},
  8.         #{item.lastName})
  9.     </foreach>
  10.     ON DUPLICATE KEY UPDATE
  11.     id=values(id),first_name=values(first_name),last_name=values(last_name)
  12. </update>
复制代码
第五种mybatis-plus提供的的批量更新

  1. default boolean updateBatchById(Collection<T> entityList) {
  2.     return this.updateBatchById(entityList, 1000);
  3. }
  4. boolean updateBatchById(Collection<T> entityList, int batchSize);
复制代码
mybatis-plus提供的批量更新是分批批量更新,默认每批1000条,可以指定分批的条数,每批执行完成后提交一下事务,不加@Transactional可能会出现第一批更新乐成了,第二批更新失败了的环境.
第六种JdbcTemplate提供的批量更新

测试代码

  1. /**
  2. * PeopleDAO继承基类
  3. */
  4. @Mapper
  5. @Repository
  6. public interface PeopleDAO extends MyBatisBaseDao<People, Long> {
  7.     void updateBatch(@Param("list") List<People> list);
  8.     void updateBatch2(List<People> list);
  9.     void updateBatch3(List<People> list);
  10.     void updateBatch4(List<People> list);
  11. }
复制代码
  1. @SpringBootTest
  2. class PeopleMapperTest {
  3.     @Resource
  4.     PeopleMapper peopleMapper;
  5.     @Resource
  6.     PeopleService peopleService;
  7.     @Resource
  8.     JdbcTemplate jdbcTemplate;
  9.     @Test
  10.     void init10wData() {
  11.         for (int i = 0; i < 100000; i++) {
  12.             People people = new People();
  13.             people.setFirstName(UUID.randomUUID().toString());
  14.             people.setLastName(UUID.randomUUID().toString());
  15.             peopleMapper.insert(people);
  16.         }
  17.     }
  18.     @Test
  19.     void updateBatch() {
  20.         List<People> list = new ArrayList();
  21.         int loop = 100;
  22.         int count = 5000;
  23.         Long maxCost = 0L;//最长耗时
  24.         Long minCost = Long.valueOf(Integer.MAX_VALUE);//最短耗时
  25.         for (int j = 0; j < count; j++) {
  26.             People people = new People();
  27.             people.setId(ThreadLocalRandom.current().nextInt(0, 100000));
  28.             people.setFirstName(UUID.randomUUID().toString());
  29.             people.setLastName(UUID.randomUUID().toString());
  30.             list.add(people);
  31.         }
  32.         Long startTime = System.currentTimeMillis();
  33.         for (int i = 0; i < loop; i++) {
  34.             Long curStartTime = System.currentTimeMillis();
  35.             // peopleMapper.updateBatch4(list);
  36.             // peopleService.updateBatchById(list);
  37.             jdbcTemplateBatchUpdate(list);
  38.             Long curCostTime = System.currentTimeMillis() - curStartTime;
  39.             if (maxCost < curCostTime) {
  40.                 maxCost = curCostTime;
  41.             }
  42.             if (minCost > curCostTime) {
  43.                 minCost = curCostTime;
  44.             }
  45.         }
  46.         System.out.println(loop + "-" + (System.currentTimeMillis() - startTime) / loop + "-" + minCost + "-" + maxCost );
  47.     }
  48.     private void jdbcTemplateBatchUpdate (List<People> list){
  49.         String sql = "update people set first_name=?,last_name=? where id = ?";
  50.         List<Object[]> params = list.stream().map(item -> new Object[]{item.getFirstName(), item.getLastName(), item.getId()}).collect(Collectors.toList());
  51.         jdbcTemplate.batchUpdate(sql,params);
  52.     }
  53. }
复制代码
参考文章:

mybatis批量更新数据三种方法效率对比 https://blog.csdn.net/q957967519/article/details/88669552
MySql中4种批量更新的方法 https://blog.csdn.net/weixin_42290280/article/details/89384741
Mysql 批量修改四种方式效率对比(一)https://blog.csdn.net/zk673820543/article/details/106579809/

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

篮之新喜

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表