MybatisPlus批量插入探索
最近在最近开发过程中遇到一个问题,在项目智慧物联网的大背景下,通常需要由多个传感器和监测设备向服务器请求,原设定是每次从mq中拉取1000数据批量插入到表分片中,在生产环境正式使用后发现最后一次成功插入时发现每次耗时需要18s,怎么会这么慢呢(—?—)赶紧多加几个分片[狗头]
JDBC配置参数rewriteBatchedStatements=true
MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。 MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。 只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL 另外这个选项对INSERT/UPDATE/DELETE都有效
我的pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>shardingjdbcdemo</artifactId> <groupId>com.atguigu</groupId> <version>0.0.1-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>mpkt</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-spring-boot-starter</artifactId> <version>4.2.0</version> </dependency> <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.8.1</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <!--指定资源的位置--> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.yml</include> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> </resources> </build> </project>View Code
我用的是saveBatch这个方法插入的
jdbc后面加上参数后有明显的效率提升
查看源码发现在不加上rewriteBatchedStatements参数时其默认的会将sql拆分并一条条发给msql
InsertBatchSomeColumn扩展插件
MybatisPlus在extension中提供了InsertBatchSomeColumn这个几个扩展方法。
AlwaysUpdateSomeColumnById: 根据Id更新每一个字段,全量更新不忽略null字段,解决mybatis-plus中updateById默认会自动忽略实体中null值字段不去更新的问题; InsertBatchSomeColumn: 真实批量插入,通过单SQL的insert语句实现批量插入; Upsert: 更新or插入,根据唯一约束判断是执行更新还是删除,相当于提供insert on duplicate key update支持。
来看看官方的说法
先不管它有没有bug我们先来试试看:
1、新建一个MySqlInjector文件继承DefaultSqlInjector将需要用的method添加进去就好了
package com.mpkt.config; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById; import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn; import com.baomidou.mybatisplus.extension.injector.methods.Upsert; import org.springframework.stereotype.Component; import java.util.List; @Component public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) { List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo); // 更新时自动填充的字段,不用插入值 methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE)); methodList.add(new AlwaysUpdateSomeColumnById()); methodList.add(new Upsert()); return methodList; } }View Code
2、新增一个名为SpiceBaseMapper的文件,把官方提供的注释copy下
3、userMapper改为继承这个新的文件就好啦
package com.mpkt.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import java.util.List; public interface SpiceBaseMapper<T> extends BaseMapper<T> { /** * 批量插入 * @param entityList * @return */ int insertBatchSomeColumn(List<T> entityList); }View Code
package com.mpkt.mapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.mpkt.entity.User; import org.apache.ibatis.annotations.Param; public interface UserMapper extends SpiceBaseMapper<User> { /** * 分页查询 * @param page * @return */ Page<User> queryAll(@Param("page") Page<User> page); /** * 创建表 * @param tableName */ void createTable(@Param("tableName") String tableName); }View Code
<?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.mpkt.mapper.UserMapper"> <select id="queryAll" resultType="com.mpkt.entity.User"> select * from user </select> <update id="createTable"> CREATE TABLE ${tableName} ( `id` bigint(255) NOT NULL, `user_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; </update> </mapper>View Code
嗯,又快了一点点,也没发现什么bug。
接下来说下rewriteBatchedStatements
的作用,这个参数的意义是:在底层jdbc是通过测试用例2这样的批量处理的方式插入数据的时候,如rewriteBatchedStatements
设为true,jdbc驱动就会把sql在本地客户端拼接成insert into table (a,b,c) values (x,x,x),(x,x,x)...
这样的sql发送到服务端(这里注意,数据量很多的时候,并不会把所有数据拼接成一条sql,而是分批次拼接,因为mysql每次传输的sql大小是有限制的),否则这样的批量插入和一条一条数据的插入没有任何区别。
其实还可以自己通过代码的方式拼接出来(这里就不介绍了),至于用那种方式就需要自己衡量了。
多线程应用分批操作
标签:MybatisPlus,批量,插入,baomidou,import,com,mybatisplus From: https://www.cnblogs.com/yiMro/p/17085124.html