Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность.
Эта статья приняла участие"Проект "Звезда раскопок"", чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам.
В предыдущей статье мы говорили о 3 методах пакетной вставки MyBatis: циклическая одиночная вставка, пакетная вставка MyBatis Plus, встроенная пакетная вставка MyBatis, нажмите, чтобы узнать подробности."MyBatis 3 способа вставки данных в пакетах! 》.
Тем не менее, предыдущие статьи также несовершенны, потому что производительность использования «одиночной вставки цикла» слишком низкая, а производительность использования «пакетной вставки MyBatis Plus» в порядке, но дополнительное введение инфраструктуры MyBatis Plus и использование «Собственная пакетная вставка MyBatis» «Вставка» имеет наилучшую производительность, но при вставке большого объема данных программа будет сообщать об ошибках, поэтому сегодня мы предоставим лучшее решение.
«Яма» нативной пакетной прошивки
Во-первых, давайте взглянем на ямы в собственной пакетной вставке MyBatis.Когда мы пакетно вставляем 100 000 фрагментов данных, код реализации выглядит следующим образом:
import com.example.demo.model.User;
import com.example.demo.service.impl.UserServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
class UserControllerTest {
// 最大循环次数
private static final int MAXCOUNT = 100000;
@Autowired
private UserServiceImpl userService;
/**
* 原生自己拼接 SQL,批量插入
*/
@Test
void saveBatchByNative() {
long stime = System.currentTimeMillis(); // 统计开始时间
List<User> list = new ArrayList<>();
for (int i = 0; i < MAXCOUNT; i++) {
User user = new User();
user.setName("test:" + i);
user.setPassword("123456");
list.add(user);
}
// 批量插入
userService.saveBatchByNative(list);
long etime = System.currentTimeMillis(); // 统计结束时间
System.out.println("执行时间:" + (etime - stime));
}
}
Код реализации в основном файле UserMapper.xml выглядит следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<insert id="saveBatchByNative">
INSERT INTO `USER`(`NAME`,`PASSWORD`) VALUES
<foreach collection="list" separator="," item="item">
(#{item.name},#{item.password})
</foreach>
</insert>
</mapper>
Когда мы успешно запустим вышеуказанную программу, появится следующая сцена:Wo, программа сообщила об ошибке!
Это связано с тем, что размер вставленного SQL с использованием встроенного пакетного вставки MyBatis составляет 4,56 МБ, а максимальный размер SQL, который может выполнить MySQL, составляет 4 МБ по умолчанию, поэтому при выполнении программы будет сообщено об ошибке.
решение
Вышеупомянутая проблема связана с тем, что файл SQL, сплайсированный во время пакетной вставки, слишком велик, поэтому выполнение MySQL сообщает об ошибке. Тогда первое решение, о котором мы подумали, состояло в том, чтобы разделить большой файл на N маленьких файлов, чтобы не сообщалось об ошибке выполнения, потому что SQL слишком велик. То есть мы можем разделить коллекцию списков, которую нужно вставить, на несколько небольших списков для выполнения операций пакетной вставки, и этот процесс операции называется сегментированием списков.
После того, как у вас есть идея обработки, следующим шагом будет практика: как разбить коллекцию?
Существует множество способов реализации операции шардирования, об этом мы поговорим позже, далее воспользуемся самым простым методом, то есть фреймворком Guava, предоставленным Google для реализации функции шардирования.
Демонстрация шардинга в действии
Чтобы реализовать функцию сегментирования, первым шагом является добавление поддержки платформы Guava и добавление следующих ссылок в pom.xml:
<!-- google guava 工具类 -->
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
Далее мы пишем небольшое демо, чтобы разделить следующие 7 имен на 3 группы (до 3 в каждой группе) Код реализации выглядит следующим образом:
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.List;
/**
* Guava 分片
*/
public class PartitionByGuavaExample {
// 原集合
private static final List<String> OLD_LIST = Arrays.asList(
"唐僧,悟空,八戒,沙僧,曹操,刘备,孙权".split(","));
public static void main(String[] args) {
// 集合分片
List<List<String>> newList = Lists.partition(OLD_LIST, 3);
// 打印分片集合
newList.forEach(i -> {
System.out.println("集合长度:" + i.size());
});
}
}
Результат выполнения вышеуказанной программы выглядит следующим образом:Как видно из приведенных выше результатов, нам нужно использовать только метод Lists.partition, предоставляемый Guava, чтобы легко разделить коллекцию.
Встроенная реализация сегментации пакетной вставки
Затем следующим шагом будет преобразование нашего кода пакетной вставки MyBatis Конкретная реализация выглядит следующим образом:
@Test
void saveBatchByNativePartition() {
long stime = System.currentTimeMillis(); // 统计开始时间
List<User> list = new ArrayList<>();
// 构建插入数据
for (int i = 0; i < MAXCOUNT; i++) {
User user = new User();
user.setName("test:" + i);
user.setPassword("123456");
list.add(user);
}
// 分片批量插入
int count = (int) Math.ceil(MAXCOUNT / 1000.0); // 分为 n 份,每份 1000 条
List<List<User>> listPartition = Lists.partition(list, count);
// 分片批量插入
for (List<User> item : listPartition) {
userService.saveBatchByNative(item);
}
long etime = System.currentTimeMillis(); // 统计结束时间
System.out.println("执行时间:" + (etime - stime));
}
Выполните приведенную выше программу, окончательный результат выполнения выглядит следующим образом:Как видно из приведенного выше рисунка, аномальная ошибка, о которой сообщалось в предыдущей пакетной вставке, исчезла, а эффективность выполнения этой реализации фактически выше, чем у MyBatis Plus. составляет:
Суммировать
В этой статье мы демонстрируем проблему встроенной пакетной вставки MyBatis: она может не работать, потому что вставлено слишком много данных. Мы можем решить эту проблему с помощью сегментирования. Этапы реализации пакетной вставки сегментирования следующие:
- Подсчитать количество осколков (разделенных на N пакетов);
- Используйте метод Lists.partition для разделения коллекции (на N коллекций);
- Цикл вставляет сегментированную коллекцию партиями.
Подпишитесь на официальный аккаунт «Сообщество китайского языка Java», чтобы увидеть больше серий статей о MyBatis и Spring Boot.