当前位置: 首页 > news >正文

【MyBatis-plus】saveBatch 性能调优和【MyBatis】的数据批量入库

总结最优的两种方法:

方法1:
使用了【MyBatis-plus】saveBatch 但是数据入库效率依旧很慢,那可能是是因为JDBC没有配置,saveBatch 批量写入并没有生效哦!!!
详细配置如下:批量数据入库:rewriteBatchedStatements=true

 # 数据源master:driver-class-name: org.postgresql.Driverurl: jdbc:postgresql://127.0.0.1:5444/mxpt_business_databases?useUnicode=true&characterEncoding=utf8&currentSchema=public&stringtype=unspecified&rewriteBatchedStatements=trueusername: postgrespassword: postgresschema: public

方法2:
使用【MyBatis】进行数据的批量入库:拼接sql语句,每1000条数据入库一次。

@Overridepublic String insertBoundValueListToDatabase(List<ResourceCalcSceneBoundValue> list){//1.先删除原有场次和工程的数据,再进行导入ResourceCalcSceneBoundValue gongkuangValue = list.get(0);Long scprodId = gongkuangValue.getScprodId();Long gongkuangId = gongkuangValue.getBoundId();List<ResourceCalcSceneBoundValue> listValue = resourceCalcSceneBoundValueMapper.selectResourceCalcSceneBoundValueList(gongkuangValue);if(listValue != null && listValue.size() > 0){resourceCalcSceneBoundValueMapper.deleteBoundValueByScprodIdAndBoundId(scprodId, gongkuangId);}//2.将结果插入到数据库中if (list.size() > 0) {//条数为1if(list.size() == 1){resourceCalcSceneBoundValueMapper.insertResourceCalcSceneBoundValueList(list.subList(0, 1));}//由于数据库对于插入字段的限制,在这里对批量插入的数据进行分批处理int batchCount = 120;//每批commit的个数int batchLastIndex = batchCount - 1;// 每批最后一个的下标for (int index = 0; index < list.size() - 1; ) {if (batchLastIndex > list.size() - 1) {batchLastIndex = list.size() - 1;resourceCalcSceneBoundValueMapper.insertResourceCalcSceneBoundValueList(list.subList(index, batchLastIndex + 1));break;// 数据插入完毕,退出循环} else {resourceCalcSceneBoundValueMapper.insertResourceCalcSceneBoundValueList(list.subList(index, batchLastIndex + 1));index = batchLastIndex + 1;// 设置下一批下标batchLastIndex = index + (batchCount - 1);}}return "边界过程数据入库成功! 条数为:"+list.size()+"条。 ";}return "数据条数为0。";}

xml代码:

<insert id="insertResourceCalcSceneBoundValueList" parameterType="java.util.List" useGeneratedKeys="false">INSERT INTO resource_calc_scene_bound_value(scprod_id, bound_id, tm, flow, water, kurong, inq, stcd, remark, jp, kaidu, kgnum)VALUES<foreach collection="list" item="item" index="index" separator=",">(#{item.scprodId,jdbcType=INTEGER},#{item.boundId,jdbcType=INTEGER},#{item.tm,jdbcType=TIMESTAMP},#{item.flow,jdbcType=NUMERIC},#{item.water,jdbcType=NUMERIC},#{item.kurong,jdbcType=NUMERIC},#{item.inq,jdbcType=NUMERIC},#{item.stcd,jdbcType=VARCHAR},#{item.remark,jdbcType=VARCHAR},#{item.jp,jdbcType=NUMERIC},#{item.kaidu,jdbcType=NUMERIC},#{item.kgnum,jdbcType=INTEGER})</foreach></insert>

参考博客:

https://www.cnblogs.com/natee/p/17428877.html
大神总结的超级详细!!!
一起学习!!!
发现接口处理速度慢的有点超出预期,感觉很奇怪,后面定位发现是数据库批量保存这块很慢。
这个项目用的是 mybatis-plus,批量保存直接用的是 mybatis-plus 提供的 saveBatch。 我点进去看了下源码,感觉有点不太对劲:
在这里插入图片描述
继续追踪了下,从这个代码来看,确实是 for 循环一条一条执行了 sqlSession.insert,下面的 consumer 执行的就是上面的 sqlSession.insert:
在这里插入图片描述
然后累计一定数量后,一批 flush。从这点来看,这个 saveBach 的性能肯定比直接一条一条 insert 快。

1、1000条数据,一条一条插入

@Test
void MybatisPlusSaveOne() {SqlSession sqlSession = sqlSessionFactory.openSession();try {StopWatch stopWatch = new StopWatch();stopWatch.start("mybatis plus save one");for (int i = 0; i < 1000; i++) {OpenTest openTest = new OpenTest();openTest.setA("a" + i);openTest.setB("b" + i);openTest.setC("c" + i);openTest.setD("d" + i);openTest.setE("e" + i);openTest.setF("f" + i);openTest.setG("g" + i);openTest.setH("h" + i);openTest.setI("i" + i);openTest.setJ("j" + i);openTest.setK("k" + i);//一条一条插入openTestService.save(openTest);}sqlSession.commit();stopWatch.stop();log.info("mybatis plus save one:" + stopWatch.getTotalTimeMillis());} finally {sqlSession.close();}
}

在这里插入图片描述
可以看到,执行一批 1000 条数的批量保存,耗费的时间是 121011 毫秒。

2、1000条数据用 mybatis-plus 自带的 saveBatch 插入

@Test
void MybatisPlusSaveBatch() {SqlSession sqlSession = sqlSessionFactory.openSession();try {List<OpenTest> openTestList = new ArrayList<>();for (int i = 0; i < 1000; i++) {OpenTest openTest = new OpenTest();openTest.setA("a" + i);openTest.setB("b" + i);openTest.setC("c" + i);openTest.setD("d" + i);openTest.setE("e" + i);openTest.setF("f" + i);openTest.setG("g" + i);openTest.setH("h" + i);openTest.setI("i" + i);openTest.setJ("j" + i);openTest.setK("k" + i);openTestList.add(openTest);}StopWatch stopWatch = new StopWatch();stopWatch.start("mybatis plus save batch");//批量插入openTestService.saveBatch(openTestList);sqlSession.commit();stopWatch.stop();log.info("mybatis plus save batch:" + stopWatch.getTotalTimeMillis());} finally {sqlSession.close();}
}

在这里插入图片描述
耗费的时间是 59927 毫秒,比一条一条插入快了一倍,从这点来看,效率还是可以的。

然后常见的还有一种利用拼接 SQL 方式来实现批量插入,我们也来对比试试看性能如何。

3、1000 条数据用手动拼接 SQL 方式插入, 搞个手动拼接:
在这里插入图片描述
来跑跑下性能如何:

@Test
void MapperSaveBatch() {SqlSession sqlSession = sqlSessionFactory.openSession();try {List<OpenTest> openTestList = new ArrayList<>();for (int i = 0; i < 1000; i++) {OpenTest openTest = new OpenTest();openTest.setA("a" + i);openTest.setB("b" + i);openTest.setC("c" + i);openTest.setD("d" + i);openTest.setE("e" + i);openTest.setF("f" + i);openTest.setG("g" + i);openTest.setH("h" + i);openTest.setI("i" + i);openTest.setJ("j" + i);openTest.setK("k" + i);openTestList.add(openTest);}StopWatch stopWatch = new StopWatch();stopWatch.start("mapper save batch");//手动拼接批量插入openTestMapper.saveBatch(openTestList);sqlSession.commit();stopWatch.stop();log.info("mapper save batch:" + stopWatch.getTotalTimeMillis());} finally {sqlSession.close();}
}

在这里插入图片描述
耗时只有 2275 毫秒,性能比 mybatis-plus 自带的 saveBatch 好了 26 倍!

这时,我又突然回想起以前直接用 JDBC 批量保存的接口,那都到这份上了,顺带也跑跑看!

4、1000 条数据用 JDBC executeBatch 插入

@Test
void JDBCSaveBatch() throws SQLException {SqlSession sqlSession = sqlSessionFactory.openSession();Connection connection = sqlSession.getConnection();connection.setAutoCommit(false);String sql = "insert into open_test(a,b,c,d,e,f,g,h,i,j,k) values(?,?,?,?,?,?,?,?,?,?,?)";PreparedStatement statement = connection.prepareStatement(sql);try {for (int i = 0; i < 1000; i++) {statement.setString(1,"a" + i);statement.setString(2,"b" + i);statement.setString(3, "c" + i);statement.setString(4,"d" + i);statement.setString(5,"e" + i);statement.setString(6,"f" + i);statement.setString(7,"g" + i);statement.setString(8,"h" + i);statement.setString(9,"i" + i);statement.setString(10,"j" + i);statement.setString(11,"k" + i);statement.addBatch();}StopWatch stopWatch = new StopWatch();stopWatch.start("JDBC save batch");statement.executeBatch();connection.commit();stopWatch.stop();log.info("JDBC save batch:" + stopWatch.getTotalTimeMillis());} finally {statement.close();sqlSession.close();}
}

在这里插入图片描述
耗时是 55663 毫秒,所以 JDBC executeBatch 的性能跟 mybatis-plus 的 saveBatch 一样(底层一样)。

综上所述,拼接 SQL 的方式实现批量保存效率最佳。

但是我又不太甘心,总感觉应该有什么别的法子,然后我就继续跟着 mybatis-plus 的源码 debug 了一下,跟到了 MySQL 的驱动,突然发现有个 if 里面的条件有点显眼:在这里插入图片描述
就是这个叫 rewriteBatchedStatements 的玩意,从名字来看是要重写批操作的 Statement,前面batchHasPlainStatements 已经是 false,取反肯定是 true,所以只要这参数是 true 就会进行一波操作。

我看了下默认是 false。
在这里插入图片描述
直接将 jdbcurl 加上了这个参数:

在这里插入图片描述
然后继续跑了下 mybatis-plus 自带的 saveBatch,果然性能大大提高,跟拼接 SQL 差不多!

在这里插入图片描述
然后我继续 debug ,来探探 rewriteBatchedStatements 究竟是怎么 rewrite 的! 如果这个参数是 true,则会执行下面的方法且直接返回:

在这里插入图片描述
看下 executeBatchedInserts 究竟干了什么:

在这里插入图片描述
看到上面我圈出来的代码没,好像已经有点感觉了,继续往下 debug。

果然!SQL 语句被 rewrite了:
在这里插入图片描述
对插入而言,所谓的 rewrite 其实就是将一批插入拼接成 insert into xxx values (a),(b),©…这样一条语句的形式然后执行,这样一来跟拼接 SQL 的效果是一样的。

那为什么默认不给这个参数设置为 true 呢?主要有以下两点:

如果批量语句中的某些语句失败,则默认重写会导致所有语句都失败。

批量语句的某些语句参数不一样,则默认重写会使得查询缓存未命中。

看起来影响不大,所以我给我的项目设置上了这个参数!

最后

稍微总结下我粗略的对比(虽然粗略,但实验结果符合原理层面的理解),如果你想更准确地做实验,可以使用 JMH,并且测试更多组数(如 5000,10000等)的情况。
在这里插入图片描述
所以如果有使用 JDBC 的 Batch 性能方面的需求,要将 rewriteBatchedStatements 设置为 true,这样能提高很多性能。

然后如果喜欢手动拼接 SQL 要注意一次拼接的数量,分批处理。

相关文章:

【MyBatis-plus】saveBatch 性能调优和【MyBatis】的数据批量入库

总结最优的两种方法&#xff1a; 方法1&#xff1a; 使用了【MyBatis-plus】saveBatch 但是数据入库效率依旧很慢&#xff0c;那可能是是因为JDBC没有配置&#xff0c;saveBatch 批量写入并没有生效哦&#xff01;&#xff01;&#xff01; 详细配置如下&#xff1a;批量数据入…...

前端三剑客之JavaScript基础入门

目录 ▐ 快速认识JavaScript ▐ 基本语法 &#x1f511;JS脚本写在哪? &#x1f511;注释 &#x1f511;变量如何声明? &#x1f511;数据类型 &#x1f511;运算符 &#x1f511;流程控制 ▐ 函数 ▐ 事件 ▐ 计时 ▐ HTML_DOM对象 * 建议学习完HTML和CSS后再…...

Fyndiq买家号下单:自养号测评如何打造本土物理环境系统?

Fyndiq 是一个瑞典电子商务平台&#xff0c;我们通过该平台为渴望讨价还价的购物者提供一系列产品。该公司为希望以可访问的方式提高销售额的所有类型的零售商提供销售渠道。Fyndiq几乎是瑞典家喻户晓的存在&#xff0c;是瑞典折扣促销平台。以销售质优价廉的商品吸引了大量忠实…...

自动检测曲别针数量:图像处理技术的应用

引言 在这篇博客中&#xff0c;我们将探讨如何使用计算机视觉技术自动检测图像中曲别针的数量。 如图&#xff1a; [1]使用灰度转换 由于彩色信息对于曲别针计数并不重要&#xff0c;我们将图像转换为灰度图&#xff0c;这样可以减少处理数据的复杂度&#xff0c;加速后续的…...

【Git】多人协作 -- 详解

一、多人协作&#xff08;1&#xff09; ⽬前&#xff0c;我们所完成的工作如下&#xff1a; 基本完成 Git 的所有本地库的相关操作&#xff0c;git 基本操作&#xff0c;分支理解&#xff0c;版本回退&#xff0c;冲突解决等等。 申请码云账号&#xff0c;将远端信息 clone…...

Eureka和Nacos有哪些区别?

Eureka和Nacos都能起到注册中心的作用&#xff0c;用法基本类似。但还是有一些区别的&#xff0c;例如&#xff1a; Nacos支持配置管理&#xff0c;而Eureka则不支持。 而且服务注册发现上也有区别&#xff0c;我们来做一个实验&#xff1a; 我们停止user-service服务&#x…...

如何正确使用 include-what-you-use

简单地说&#xff0c;由 Google 开发的 include-what-you-use&#xff08;IWYU&#xff09;让源代码文件包含代码里用到的所有头文件。这种方法确保在改动了一些接口之后&#xff0c;代码依然最有可能编译成功。 之前我写了一篇关于 include-what-you-use 工具的文章&#xff…...

企业内网安全软件分享,有什么内网安全软件

内网安全&#xff1f; 其实就是网络安全的一种。 什么是内网安全软件&#xff1f; 内网安全软件是企业保障内网安全的一种重要工具。 它主要帮助企业实现对网络设备、应用程序、用户行为等方面的监控和管理&#xff0c;以预防和应对各种网络攻击。 这类软件主要用于对内网中…...

【摘葡萄game】

您想要了解的“摘葡萄游戏”可能是一个编程项目或者是一个编程相关的练习。我可以提供一个简单的摘葡萄游戏的思路和代码示例。这个游戏可以用多种编程语言来实现&#xff0c;比如Python、Java等。这里我以Python为例&#xff0c;给出一个基础版本的摘葡萄游戏的概念和代码。 …...

java如何实现字符串连接

在java中&#xff0c;字符串与字符串连接可以用运算符和 比如有字符串a,字符串b 想要把a和b连接起来&#xff0c;定义一个字符串变量c cab 或者 ab 示例代码 public class Zifuchuanlianjie {public static void main(String[] args) {String a"我叫李狗蛋";S…...

流量卡选卡攻略,拯救不会选流量卡的小白!

​ 家人们&#xff0c;你们知道不&#xff0c;选择一款性价比高的流量卡&#xff0c;真的超级省钱。 一、首先&#xff0c;说一说申请。 运营商推出线上流量卡&#xff0c;注意是线上的流量卡&#xff0c;都是免费领取&#xff0c;运营商包邮到家&#xff0c;在激活充值之前不…...

python class __format__ __bytes__区别

在Python中&#xff0c;__format__和__bytes__是两个特殊方法&#xff0c;它们允许对象自定义它们在特定情境下的字符串表示。以下是这两个方法的区别和作用&#xff1a; __format__ 作用&#xff1a;__format__方法用于定义对象在使用format()函数或格式化字符串&#xff08…...

C++ | Leetcode C++题解之第134题加油站

题目&#xff1a; 题解&#xff1a; class Solution { public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int n gas.size();int i 0;while (i < n) {int sumOfGas 0, sumOfCost 0;int cnt 0;while (cnt < n) {int j (i …...

【Linux】ls命令

这个命令主要是用于显示指定工作目录下之内容&#xff08;列出目前工作目录所含的文件及子目录)。 掌握几个重点的常使用的就可以&#xff1a; ls -l # 以长格式显示当前目录中的文件和目录 ls -a # 显示当前目录中的所有文件和目录&am…...

多态、虚函数表与动态绑定的深入解析

目录 多态简介 虚函数表与动态绑定 虚函数表 动态绑定机制 内存与性能影响 纯虚函数与抽象类 纯虚函数 抽象类 动态类型转换与typeid操作符 dynamic_cast typeid操作符 虚析构函数的重要性 在面向对象编程中&#xff0c;多态性是一种核心特性&#xff0c;它允许我们…...

VitePress+Docker+jenkins构建个人网站

VitePress官网 VitePress | 由 Vite 和 Vue 驱动的静态站点生成器 可以理解为一个前端脚手架:快速生成个人站点 最好先大概看一遍 快速开始 | VitePress 可以在线体验一下 安装条件 node -v 检查下node版本 在D盘创建一个文件夹 例如:VitePress 进入文件夹 cmd npm ini…...

Windows11下Docker使用记录(五)

目录 准备1. WSL安装cuda container toolkit2. win11 Docker Desktop 设置3. WSL创建docker container并连接cuda4. container安装miniconda&#xff08;可选&#xff09; Docker容器可以从底层虚拟化&#xff0c;使我们能够在 不降级 CUDA驱动程序的情况下使用 任何版本的CU…...

快速学习Java的多维数组技巧

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…...

C语言运算类型有哪些

C语言中的运算类型主要分为以下几类&#xff1a; 1. 算术运算符&#xff1a; - 加法运算符 - 减法运算符 - - 乘法运算符 * - 除法运算符 / - 取模运算符 %&#xff08;取余数&#xff09; 2. 关系运算符&#xff1a; - 大于 > - 小于 < - 大…...

【深度学习】Loss为Nan的可能原因

文章目录 1. 问题情境2. 原因分析3. 导致Loss为Nan的其他可能原因 1. 问题情境 在某个网络架构下&#xff0c;我为某个数据项引入了一个损失函数。 这个数据项是nn.Embedding类型的&#xff0c;我加入的损失函数是对nn.Embedding空间做约束。 因为我在没加入优化loss前&#x…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...