MybatisPlus 大批量数据插入缓慢问题

         一般在做业务的增删改查时,都会遇到大量数据插入的问题。如果数据记录达到几万条时,系统服务在数据入库时会卡的非常难受。如何快速的插入数据呢?

首先,我们都会想到进行批量插入数据,但是批量插入之间也有很大的差异。

        本文对比MybatisPlus 自带 saveBatch()方法、SqlSession 批量插入方式、自定义foreach方式、多线程foreach()四种方式:

公共配置

获取数据:

/**

 * 获取数据

 */

private List<FeedbackEmp> getData() {

   List<FeedbackEmp> list = new ArrayList<>();

   FeedbackEmp entity;

   for (int i = 0; i < 300000; i++) {

      entity = new FeedbackEmp();

      entity.setId(i + "");

      entity.setFeedbackId("0");

      entity.setEmpId("0");

      list.add(entity);

   }

   return list;

}

Mapper xml SQL:

<insert id="insertBatch" parameterType="list">

    INSERT INTO t_supervise_feedback_emp(c_id,c_feedback_id,c_emp_id)

    VALUES

    <foreach collection="list" item="item" separator=",">

        (#{item.id},#{item.feedbackId},#{item.empId})

    </foreach>

</insert>

Spring 配置:

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">

    <constructor-arg ref="sqlSessionFactory"/>

    <constructor-arg value="BATCH"/>

</bean>

(一)saveBatch()方法

/**

 * MybatisPlus 自带批量

 */

@Test

public void addBatch() {

   try {

      long start = System.currentTimeMillis();

      List<FeedbackEmp> list = this.getData();

      feedbackEmpService.saveBatch(list, list.size());

      long end = System.currentTimeMillis();

      System.out.println("插入耗时:--------------------------" + (start - end) + "--------------------------");

   } catch (Exception e) {

      e.printStackTrace();

   }

}

     (二)SqlSession 批量插入方式

/**

 * SqlSession 批量

 */

@Test

public void addSession() {

   SqlSession sqlSession = null;

   try {

      sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);

      FeedbackEmpMapper mapper = sqlSession.getMapper(FeedbackEmpMapper.class);

      long start = System.currentTimeMillis();

      List<FeedbackEmp> list = this.getData();

      mapper.insertBatch(list);

      sqlSession.commit();

      long end = System.currentTimeMillis();

      System.out.println("插入耗时:--------------------------" + (start - end) + "--------------------------");

   } catch (Exception e) {

      e.printStackTrace();

   } finally {

      if (sqlSession != null) {

         sqlSession.close();

      }

   }

}

(三)自定义foreach方式

/**

 * foreach 批量

 */

@Test

public void addForeach() {

   try {

      long start = System.currentTimeMillis();

      List<FeedbackEmp> list = this.getData();

      feedbackEmpMapper.insertBatch(list);

      long end = System.currentTimeMillis();

      System.out.println("插入耗时:--------------------------" + (start - end) + "--------------------------");

   } catch (Exception e) {

      e.printStackTrace();

   }

}

(四)多线程foreach

/**

    * foreach 多线程

    */

   @Test

   public void addForeachExecutor() {

      try {

         long start = System.currentTimeMillis();

         List<FeedbackEmp> list = this.getData();

         this.exec(list, 1000);

         long end = System.currentTimeMillis();

         System.out.println("插入耗时:--------------------------" + (start - end) + "--------------------------");

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

   /**

    * 多线程处理

    *

    * @param list     数据

    * @param dealSize 每个线程处理数量

    * @throws Exception

    */

   private void exec(List<FeedbackEmp> list, int dealSize) throws Exception {

      if (!CollectionUtils.isEmpty(list)) {

         //数据总的大小

         int count = list.size();

         //每个线程数据集

         List<FeedbackEmp> threadList = null;

         //线程池

         int runSize = (count / dealSize) + 1;

         ThreadPoolExecutor executor = new ThreadPoolExecutor(

               runSize,

               350,

               30L, TimeUnit.SECONDS,

               new SynchronousQueue<>());

         //计数器

         CountDownLatch countDownLatch = new CountDownLatch(runSize);

         for (int i = 0; i < runSize; i++) {

            //计算每个线程执行的数据

            int startIndex = (i * dealSize);

            if ((i + 1) == runSize) {

               int endIndex = count;

               threadList = list.subList(startIndex, endIndex);

            } else {

               int endIndex = (i + 1) * dealSize;

               threadList = list.subList(startIndex, endIndex);

            }

            //线程任务

            MyThread myThread = new MyThread(feedbackEmpMapper, threadList, countDownLatch);

            executor.execute(myThread);

         }

         //计数

         countDownLatch.await();

         //关闭线程池

         executor.shutdown();

      }

   }

/**

 * 自定义线程

 */

class MyThread extends Thread {

   private FeedbackEmpMapper feedbackEmpMapper;

   private List<FeedbackEmp> list;

   private CountDownLatch countDownLatch;

   MyThread(FeedbackEmpMapper feedbackEmpMapper, List<FeedbackEmp> list, CountDownLatch countDownLatch) {

      this.feedbackEmpMapper = feedbackEmpMapper;

      this.list = list;

      this.countDownLatch = countDownLatch;

   }

   @Override

   public void run() {

      if (!CollectionUtils.isEmpty(list)) {

         feedbackEmpMapper.insertBatch(list);

      }

      countDownLatch.countDown();

   }

对比结果和结论:

saveBatch()方法

SqlSession 批量插入方式

自定义foreach方式

多线程foreach

500条

5427ms

394ms

316ms

---

1000条

12680ms

552ms

467ms

---

5000条

44951ms

2408ms

1613ms

1744ms

10000条

103219ms

3447ms

2435ms

2164ms

50000条

---

6038ms

4530ms

3969ms

100000条

---

11930ms

9731ms

8335ms

300000条

---

34280ms

29446ms

19507ms

所以 数据量小的时候建议使用 foreach方式,数据量大的时候使用 多线程foreach方式。如果有更快速的方式请留言,一起学习~

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值