Предисловие: Функция «Пакетная доставка» сегодня является одной из наиболее распространенных бизнес-функций в нашей повседневной работе.Старшийприди один MyBatis Сводка пакетной вставки, и в то же время 3 Проведите тест производительности этого метода реализации и соответствующий анализ принципов.
Снова входя в процветающий мир, толпы толпятся, глаза пусты, а вера вечна, все они за Китай.
Оглавление
1. Циклическая одиночная вставка
②Реализация уровня бизнес-логики
③ Реализация уровня сохранения данных.
3. Собственная пакетная вставка
① Расширение уровня бизнес-логики
② Расширение уровня сохранения данных
Собственный тест производительности пакетной вставки
Давайте сначала кратко об этом поговорим 3 种Пакетная Функции вставки:
Прежде чем мы начнем, давайте сначала создадим базу данных и протестируем данные. Выполняемый сценарий SQL выглядит следующим образом:
-- ----------------------------
-- создать базу данных
-- ----------------------------
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP DATABASE IF EXISTS `testdb`;
CREATE DATABASE `testdb`;
USE `testdb`;
-- ----------------------------
-- создавать user поверхность
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
`createtime` datetime NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- добавить в тестовых данных
-- ----------------------------
INSERT INTO `user` VALUES (1, «Чжао Юнь», '123456', '2021-09-10 18:11:16');
INSERT INTO `user` VALUES (2, «Чжан Фэй», '123456', '2021-09-10 18:11:28');
INSERT INTO `user` VALUES (3, «Гуань Юй», '123456', '2021-09-10 18:11:34');
INSERT INTO `user` VALUES (4, «Лю Бэй», '123456', '2021-09-10 18:11:41');
INSERT INTO `user` VALUES (5, «Цао Цао», '123456', '2021-09-10 18:12:02');
SET FOREIGN_KEY_CHECKS = 1;
Конечный эффект от базы данных следующий:
Далее мы будем использовать Spring Boot проект,Пакетная вставка 10W данные для проверки времени выполнения каждого метода отдельно.
(Тестовый) основной код для одной вставки в цикл выглядит следующим образом:
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;
@SpringBootTest
class UserControllerTest {
// Максимальное количество циклов
private static final int MAXCOUNT = 100000;
@Autowired
private UserServiceImpl userService;
/**
* Петля одинарная вставка
*/
@Test
void save() {
long stime = System.currentTimeMillis(); // Время начала статистики
for (int i = 0; i < MAXCOUNT; i++) {
User user = new User();
user.setName("test:" + i);
user.setPassword("123456");
userService.save(user);
}
long etime = System.currentTimeMillis(); // Время окончания статистики
System.out.println("Время выполнения:" + (etime - stime));
}
}
Запуск вышеуказанной программы занял 88574 миллисекунды, как показано на рисунке ниже:
Существует три основных класса реализации функции «Пакетная вставка MP»: UserController (контроллер).、UserServiceImpl (класс реализации бизнес-логики), UserMapper (класс отображения базы данных),Процесс их вызова выглядит следующим образом:
Обратите внимание, что для реализации этого метода требуется следующее содержимое в файле pom.xml, добавленное в MP рама, открытая pom.xml:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>mybatis-plus-latest-version</version>
</dependency>
Примечание. mybatis-plus-latest-version представляет собой номер последней версии платформы MP. Вы можете посетить https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter, чтобы запросить номер последней версии. но при использовании не забудьте заменить «mybatis-plus-latest-version» выше на конкретный номер версии, например 3.4.3, чтобы правильно представить структуру.
Для получения дополнительной информации о структуре MP посетите ее официальный сайт: https://baomidou.com/guide/.
import com.example.demo.model.User;
import com.example.demo.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/u")
public class UserController {
@Autowired
private UserServiceImpl userService;
/**
* Пакетная вставка (под заказ)
*/
@RequestMapping("/mysavebatch")
public boolean mySaveBatch(){
List<User> list = new ArrayList<>();
// обращатьсядобавить в (пользовательские) данные
for (int i = 0; i < 1000; i++) {
User user = new User();
user.setName("test:"+i);
user.setPassword("123456");
list.add(user);
}
return userService.saveBatchCustom(list);
}
}
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.mapper.UserMapper;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User>
implements UserService {
@Autowired
private UserMapper userMapper;
public boolean saveBatchCustom(List<User> list){
return userMapper.saveBatchCustom(list);
}
}
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.model.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper extends BaseMapper<User>{
boolean saveBatchCustom(List<User> list);
}
После реализации приведенного выше кода мы можем использовать MP Для реализации пакетной вставка функции, но помимо конкретного кода реализации в этой статье нам также необходимо знать эффективность выполнения каждого метода, поэтому далее мы напишем MP тестовый код.
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;
/**
* MP Пакетная вставка
*/
@Test
void saveBatch() {
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);
}
// MP Пакетная вставка
userService.saveBatch(list);
long etime = System.currentTimeMillis(); // Время окончания статистики
System.out.println("Время выполнения:" + (etime - stime));
}
}
Выполнение вышеуказанной программы заняло в общей сложности 6088 миллисекунд, как показано на рисунке ниже:
Из приведенных выше результатов видно, что использование MP из Пакетная функция вставки (вставить данные 10W bar), его производительность улучшена по сравнению с производительностью одиночной вставки в цикл. 14.5 раз.
По времени выполнения MP и одиночной вставке цикла мы видим, что использование MP не является выполнением одного цикла, как думают некоторые друзья. Чтобы объяснить эту проблему более четко, мы рассмотрели исходный код MP.
Основным кодом реализации MP является метод saveBatch. Исходный код этого метода выглядит следующим образом:
Давайте продолжим следить за перегруженным методом saveBatch:
Как видно из приведенного выше исходного кода, MP заключается в разделении данных, подлежащих выполнению, на N порция, каждая порция 1000 полоски, каждая полная 1000 Статья будет выполнена один раз. вставка,所以它из性能要比Петля одинарная вставкаиз性能高很多。
Так почему же нам нужно выполнять его пакетами, а не все сразу? Не волнуйтесь, мы поймем, когда рассмотрим третий метод реализации.
Родной Пакетная метод доставки заключается в том, чтобы полагаться на MyBatis в foreach тег, объединяющий данные в собственный insert Оператор выполняется один раз, а основной код реализации выглядит следующим образом.
существовать UserServiceImpl добавить в saveBatchByNative Метод, код реализации следующий:
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.mapper.UserMapper;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
@Autowired
private UserMapper userMapper;
public boolean saveBatchByNative(List<User> list) {
return userMapper.saveBatchByNative(list);
}
}
существовать UserMapper добавить в saveBatchByNative Метод, код реализации следующий:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.model.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper extends BaseMapper<User> {
boolean saveBatchByNative(List<User> list);
}
Создайте файл UserMapper.xml и используйте тег foreach для объединения SQL. Конкретный код реализации выглядит следующим образом:
<?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>
После вышеописанных действий наша родная Пакетная Функция вставки почти реализована. Далее используем Модульное. Хочу проверить эффективность выполнения этого метода.
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));
}
}
Однако когда мы запускаем программу, происходит следующее:
Нани? Выполнение программы фактически сообщило об ошибке.
Как видно из приведенного выше сообщения об ошибке, когда мы используем собственный метод для объединения фрагментов данных мощностью 10 Вт в один SQL для выполнения, объединенный SQL слишком велик (4,56 МБ), что приводит к ошибке выполнения программы, поскольку по умолчанию максимальный размер SQL, который может выполнить MySQL (размер), составляет 4 МБ, поэтому программа сообщила об ошибке.
Это оригинальная Пакетная Недостатки метода вставки и почему MP Причина пакетного выполнения состоит в том, чтобы предотвратить запуск базы данных «Максимальное», когда программа существует. исполнение SQL Это приводит к тому, что выполнение программы сообщает об ошибке.
решение
Конечно, мы также можем решить проблему ошибок, установив максимальное выполнение SQL MySQL. Команда настройки выглядит следующим образом:
-- Установить максимальное исполнение SQL для 10M
set global max_allowed_packet=10*1024*1024;
Как показано ниже:
ПРИМЕЧАНИЕ. Для приведенной выше команды требуется наличие MySQL Выполняется в подключенном клиенте.
Но приведенное выше решение по-прежнему лечит симптомы, а не первопричину.,потому чтодля我们无法预测程序中最大из执行 SQL Насколько он велик, то наиболее универсальный метод — распределить выполнение Пакетная метод вставки (то есть типа MP реализовано).
Когда мы устанавливаем максимальный размер исполняемого SQL-кода MySQL равным 10 МБ и запускаем приведенный выше код модульного теста, результаты выполнения будут следующими:
В этой статье мы представили MyBatis Пакетная вставкаиз 3 метод, среди которого циклическая одиночная вставка имеет самую низкую производительность и является наименее желательной для использования; MyBatis сплайсинг родной SQL Метод однократной вставки имеет самую высокую производительность, но этот метод может вызвать ошибки выполнения программы (запуск максимального выполнения базы данных). SQL ограничение размера), поэтому, учитывая вышеуказанные обстоятельства, вы можете рассмотреть возможность использования MP из Пакетная вставка Функция。
Наконец Старший Я считаю, что все, должно быть, заплатили плату за обучение MyBatis Пакетная 3 метода вставки и метод, который занимает всего 2 секунды для 100 000 фрагментов данных, сосредоточьтесь на Старший Не теряйтесь В следующем выпуске я научу вас практическим навыкам Более~~.