О пакетных обновлениях для mybatis

#java #mysql #jvm #mybatis

#java #mysql #jvm #mybatis

Вопрос:

Мне нужны данные для пакетного обновления, поэтому я использую два метода. 1-Е обновление в цикле «для», и все за одну транзакцию. И 2-й, объединяющий SQL «случай, когда» в XML mybatis. Я думаю, что 1-й будет высокоэффективным, но мой тестовый вывод не в том, что, например, когда я обновляю 5000 данных, эффективность этих методов одинакова, но 2-й метод использует слишком много памяти jvm, в частности, G1 Old Gen и G1 Survivor Space, и он не может быть переработан вовремя. Я не знаю, почему это так, пожалуйста, дайте мне свой ответ.

Проект основан на Springboot 2.0.6, использует MySQL 5.

1-й способ, обновление в цикле «для».

     @Transactional
    public void testUpdate(){
        User user;
        long a = System.currentTimeMillis();
        for(int i = 1; i < 5000; i  ){
            user = new User();
            user.setId(i);
            user.setUsername(RandomStringUtils.randomAlphanumeric(5));
            user.setEmail(RandomStringUtils.randomAlphanumeric(5));
            user.setPassword(RandomStringUtils.randomAlphanumeric(5));
            user.setPhone(RandomStringUtils.randomAlphanumeric(5));
            userMapper.updateByPrimaryKey(user);
        }
        long b = System.currentTimeMillis();
        System.out.println("testUpdateT  ************************     "   (b-a));
    }
  

2-й метод, объединяющий SQL ‘case-when’ в XML mybatis.

         public void testUpdateBatch(){
        User user;
        long a = System.currentTimeMillis();
        List<User> list = new ArrayList<>();
        for(int i=1;i<5000;i  ){
            user = new User();
            user.setId(i);
            user.setUsername(RandomStringUtils.randomAlphanumeric(5));
            user.setEmail(RandomStringUtils.randomAlphanumeric(5));
            user.setPassword(RandomStringUtils.randomAlphanumeric(5));
            user.setPhone(RandomStringUtils.randomAlphanumeric(5));
            list.add(user);
        }
        userMapper.updateBatch(list);
        long b = System.currentTimeMillis();
        System.out.println("testUpdateT  ************************     "   (b-a));
    }
  

sql

   <update id="updateBatch">
    update user
    <trim prefix="set" suffixOverrides=",">
      <trim prefix="username =case" suffix="end,">
        <foreach collection="list" item="i" index="index">
          when id=#{i.id} then #{i.username}
        </foreach>
      </trim>
      <trim prefix=" password =case" suffix="end,">
        <foreach collection="list" item="i" index="index">
          when id=#{i.id} then #{i.password}
        </foreach>
      </trim>
      <trim prefix="email =case" suffix="end," >
        <foreach collection="list" item="i" index="index">
          when id=#{i.id} then #{i.email}
        </foreach>
      </trim>
      <trim prefix="phone =case" suffix="end," >
        <foreach collection="list" item="i" index="index">
          when id=#{i.id} then #{i.phone}
        </foreach>
      </trim>
    </trim>
    where
    <foreach collection="list" separator="or" item="i" index="index" >
      id=#{i.id}
    </foreach>
  </update>
  

В этой таблице 27000 элементов, 1-й стоит 5500 мс, а 2-й — 8000-10000 мс, и, что наиболее важно, после завершения 2-го метода использование памяти кучи увеличивается, и она не может быть собрана за короткий промежуток времени.

Комментарии:

1. Используете ли вы BATCH executor в первом методе? Когда вы говорите, что второй метод can't recycle in time что именно вы имеете в виду? Когда вы ожидаете освобождения памяти?

2. Я не использую batch executor. Теперь я знаю, что большой объект напрямую перейдет к старому поколению, поэтому до следующего полного GC он не может быть переработан. Спасибо за ответ!