Mybatis-Plus多种批量插入方案对比
背景
六月某日上线了一个日报表任务,因是第一次上线,故需要为历史所有日期都初始化一次报表数据
在执行过程中发现新增特别的慢:插入十万条左右的数据,SQL执行耗费高达三分多钟
因很早就听闻过mybatis-plus的[伪]批量新增的问题,很快锁定问题并进行修复,下面细节描述多种批量新增方案的具体性能表现
# 测试表结构
DROP TABLE IF EXISTS `biz_batch_insert_test`;
CREATE TABLE `biz_batch_insert_test` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(600) DEFAULT NULL,`age` tinyint(4) unsigned DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;# name字段给的长度大了些,模拟生产实际表结构占用
# 测试库版本:5.7.5
方案一:传统for循环
@Test
public void testUserInsert() {long l = System.currentTimeMillis();for (int i = 0; i < 100000; i++) {TestUser testUser = new TestUser();testUser.setName("中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国");testUser.setAge(20);testUserService.save(testUser);}System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}# 插入10万条耗时:238040ms,大约4分钟
方案二:使用Mybatis-Plus的saveBatch
select * from oss_group where id_path like ‘0.1.%’;
@Test
public void testUserInsert() {long l = System.currentTimeMillis();List<TestUser> list = new ArrayList<>();for (int i = 0; i < 100000; i++) {TestUser testUser = new TestUser();testUser.setName("中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国");testUser.setAge(20);list.add(testUser);}testUserService.saveBatch(list);System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}# 插入耗时:62180ms,大约1分钟
这里先留下一张saveBatch的SQL日志截图,这里的日志是一个insert into语句,下面带了一千条数据(MP默认一千条一个批次),使用Mybatis Log插件查看还是单条的SQL

方案三:在方案二的基础上修改MySQL连接参数:rewriteBatchedStatements=true
# 与方案二测试代码相同
# 插入耗时:35260ms,大约半分钟
其SQL日志也如上方案二所示,MySQL Jdbc驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,直接造成较低的性能
Mysql连接配置链接

方案四:使用Mybatis-Plus提供的扩展插件:InsertBatchSomeColumn
@Test
public void testUserInsert() {long l = System.currentTimeMillis();List<TestUser> list = new ArrayList<>();for (int i = 0; i < 10000; i++) {TestUser testUser = new TestUser();testUser.setName("中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国中国");testUser.setAge(20);list.add(testUser);// 因为mysql的参数max_allowed_packet限制,所以这里程序改成单批1000条if(list.size() == 1000){testUserMapper.insertBatchSomeColumn(list);list.clear();}}System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}# 插入耗时:24410ms,大约24秒
再来看看此时控制台的SQL,与方案二的SQL有着明显的区别,这里是将批次插入的数据拼接在同一条SQL中,对于MySQL处理来说,这是真的批量新增

配置方式
// 1. 自定义SQL注入器
public class BatchSaveSqlInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {// 注意:保留mybatis-plus的自带方法List<AbstractMethod> methodList = super.getMethodList(mapperClass);methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));return methodList;}
}// 2. 实现自定义baseMapper
public interface BatchSaveBaseMapper<T> extends BaseMapper<T> {/*** 批量插入 仅适用于mysql** @param entityList* 实体列表* @return 影响行数*/Integer insertBatchSomeColumn(Collection<T> entityList);
}// 3. 注入插件
// 方式一
@Configuration
public class MybatisPlusConfig {/*** 分页插件*/@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));return paginationInterceptor;}/*** SQL注入器*/@Beanpublic BatchSaveSqlInjector easySqlInjector() {return new BatchSaveSqlInjector();}
}// 方式二
// 若项目有自定义SqlSessionFactory,也可在初始化时将自定义SQL注入器植入,参考下图MybatisPlusAutoConfiguration.sqlSessionFactory的做法

总结
| 插入10万数据耗时(秒) | |
|---|---|
| mybatis-plus:save | 238 |
| mybatis-plus的saveBatch:rewriteBatchedStatements=false | 62 |
| mybatis-plus的saveBatch:rewriteBatchedStatements=true | 35 |
| mybatis-plus扩展插件:InsertBatchSomeColumn | 24 |
相关文章:
Mybatis-Plus多种批量插入方案对比
背景 六月某日上线了一个日报表任务,因是第一次上线,故需要为历史所有日期都初始化一次报表数据 在执行过程中发现新增特别的慢:插入十万条左右的数据,SQL执行耗费高达三分多钟 因很早就听闻过mybatis-plus的[伪]批量新增的问题&…...
数据库面试
1. 简单介绍一下Spring中的事务管理。 答:事务就是对一系列的数据库操作(比如将insert,delete,update,select多条sql语句)作为一个整体执行,进行统一的提交或回滚操作,如果这组sql语…...
探索Web Components
title: 探索Web Components date: 2024/6/16 updated: 2024/6/16 author: cmdragon excerpt: 这篇文章介绍了Web Components技术,它允许开发者创建可复用、封装良好的自定义HTML元素,并直接在浏览器中运行,无需依赖外部库。通过组合HTML模…...
摄影师在人工智能竞赛中与机器较量并获胜
摄影师在人工智能竞赛中与机器较量并获胜 自从生成式人工智能出现以来,由来已久的人机大战显然呈现出一边倒的态势。但是有一位摄影师,一心想证明用人眼拍摄的照片是有道理的,他向算法驱动的竞争对手发起了挑战,并取得了胜利。 迈…...
CMU最新论文:机器人智慧流畅的躲避障碍物论文详细讲解
CMU华人博士生Tairan He最新论文:Agile But Safe: Learning Collision-Free High-Speed Legged Locomotion 代码开源:Code: https://github.com/LeCAR-Lab/ABS B站实际效果展示视频地址:bilibili效果地址 我会详细解读论文的内容,让我们开始吧…...
Spring中自定义注解进行类方法增强
说明 说到对类方法增强,第一时间想到自定义注解,通过aop切面进行实现。这是一种常用做法,但是在某些场景下,如开发公共组件,定义aop切面可能不是最优方案。以后通过原生aop方式,自定义注解,对类…...
TS:元组
问: 解释下什么是元组 回答: 元组(Tuple)是一种数据结构,类似于数组,但与数组不同的是,元组中的元素类型可以各不相同,且元组的长度是固定的。元组在许多编程语言中都有实现,包括 TypeScript…...
微服务 | Springboot整合Dubbo+Nacos实现RPC调用
官网:Apache Dubbo 随着互联网技术的飞速发展,越来越多的企业和开发者开始关注微服务架构。微服务架构可以将一个大型的应用拆分成多个独立、可扩展、可维护的小型服务,每个服务负责实现应用的一部分功能。这种架构方式可以提高开发效率&…...
读书的意义
...
第66集《摄大乘论》
请大家打开《讲义》第二二二页: 庚九、念(分二:辛一正念法身;辛二兼显净土) 辛一、正念法身(分二:壬一征;壬二释) 壬一、征 这个是讲到十门分别(二0三页),分别清净法身的第九段,讲到念&…...
VMware 桥接网络突然无法上网
VMware 桥接网络突然无法上网 0. 问题1. 解决方法 0. 问题 昨天,VMware 桥接网络正常使用,今天突然无法上网。 1. 解决方法 打开VMware的虚拟网络编辑器,将桥接模式的网络从“自动”改成你要使用的网卡,问题解决。 完成&#…...
面试题——Redis
★1.简述一下缓存穿透,缓存击穿,缓存雪崩 ? 缓存穿透:大量恶意请求一个不存在的数据,使得压力绕过Redis缓存层打到数据库,造成数据库瘫痪 处理:①设置黑名单,维护一个可能存在也可能不存在的黑名单数据列表,对请求进行过滤(简单高效) ②布隆过滤器,会出现误删,且相对麻烦(不…...
Java——构造器(构造方法)和 this
一、什么是构造器 构造器(Constructor)是Java类的一种特殊方法,用于初始化对象的状态。构造器在创建对象时被调用,可以对对象的成员变量进行初始化。 我之前的文章《Java——类和对象-CSDN博客》中也提到了构造器。 二、构造器…...
MySQL-连接查询
049-内连接之等值连接 案例:查询每个员工所在的部门名称,要求显示员工名、部门名。 select e.ename, d.dname from emp e inner join dept d on e.deptnod.deptno;注意:inner可以省略 select e.ename, d.dname from emp e join dept d on…...
适合小白学习的项目1832javaERP管理系统之仓库采购管理Myeclipse开发mysql数据库servlet结构java编程计算机网页项目
一、源码特点 java erp管理系统之仓库采购管理是一套完善的web设计系统,对理解JSP java编程开发语言有帮助采用了serlvet设计,系统具有完整的源代码和数据库,系统采用web模式,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Mye…...
分布式技术导论 — 探索分析从起源到现今的巅峰之旅(分布式技术)
分析探索从起源到现今的巅峰之旅 背景介绍数据可伸缩性案例 计算可伸缩性案例 代价和权衡分布式的代价分布式的权衡权衡策略 分布式技术方向数据系统运算系统 分布式数据系统Partition(分区)Round-Robin(轮询)局限性 Range&#x…...
基于Python+OpenCV+SVM车牌识别系统(GUI界面)【W3】
简介: 随着交通管理的日益复杂化和智能化需求的增加,车牌识别系统在安防、智慧交通管理等领域中扮演着重要角色。传统的车牌识别系统主要基于图像处理和模式识别技术,随着计算机视觉技术的发展,基于Python、OpenCV和机器学习算法的…...
ansible.cfg forks参数
在Ansible的配置文件ansible.cfg中,forks参数是一个非常关键的设置,它控制了Ansible执行任务时的并发连接数,直接影响到Ansible执行 playbook 或 ad-hoc 命令时的速度和效率。 意义与作用 并发控制:当你使用Ansible来管理多台主…...
Web前端写随机抽奖:技术与创意的碰撞
Web前端写随机抽奖:技术与创意的碰撞 在Web前端的世界里,随机抽奖功能不仅是一种常见的交互元素,更是技术与创意的完美结合。下面,我们将从四个方面、五个方面、六个方面和七个方面,深入探讨Web前端实现随机抽奖的技术…...
Centos系统yum安装mysql数据库
安装之前需要将系统自带的mariadb-libs软件包删除。 检查是否存在mariadb-libs包。 yum list installed|grep mariadb-libs 删除mariadb-libs包 yum -y remove mariadb-libs 声明: 系统:CentOS-7-x86_64-DVD-2009 安装为最小化安装,没…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...
