【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¤tSchema=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】的数据批量入库
总结最优的两种方法: 方法1: 使用了【MyBatis-plus】saveBatch 但是数据入库效率依旧很慢,那可能是是因为JDBC没有配置,saveBatch 批量写入并没有生效哦!!! 详细配置如下:批量数据入…...
前端三剑客之JavaScript基础入门
目录 ▐ 快速认识JavaScript ▐ 基本语法 🔑JS脚本写在哪? 🔑注释 🔑变量如何声明? 🔑数据类型 🔑运算符 🔑流程控制 ▐ 函数 ▐ 事件 ▐ 计时 ▐ HTML_DOM对象 * 建议学习完HTML和CSS后再…...
Fyndiq买家号下单:自养号测评如何打造本土物理环境系统?
Fyndiq 是一个瑞典电子商务平台,我们通过该平台为渴望讨价还价的购物者提供一系列产品。该公司为希望以可访问的方式提高销售额的所有类型的零售商提供销售渠道。Fyndiq几乎是瑞典家喻户晓的存在,是瑞典折扣促销平台。以销售质优价廉的商品吸引了大量忠实…...
自动检测曲别针数量:图像处理技术的应用
引言 在这篇博客中,我们将探讨如何使用计算机视觉技术自动检测图像中曲别针的数量。 如图: [1]使用灰度转换 由于彩色信息对于曲别针计数并不重要,我们将图像转换为灰度图,这样可以减少处理数据的复杂度,加速后续的…...
【Git】多人协作 -- 详解
一、多人协作(1) ⽬前,我们所完成的工作如下: 基本完成 Git 的所有本地库的相关操作,git 基本操作,分支理解,版本回退,冲突解决等等。 申请码云账号,将远端信息 clone…...
Eureka和Nacos有哪些区别?
Eureka和Nacos都能起到注册中心的作用,用法基本类似。但还是有一些区别的,例如: Nacos支持配置管理,而Eureka则不支持。 而且服务注册发现上也有区别,我们来做一个实验: 我们停止user-service服务&#x…...
如何正确使用 include-what-you-use
简单地说,由 Google 开发的 include-what-you-use(IWYU)让源代码文件包含代码里用到的所有头文件。这种方法确保在改动了一些接口之后,代码依然最有可能编译成功。 之前我写了一篇关于 include-what-you-use 工具的文章ÿ…...
企业内网安全软件分享,有什么内网安全软件
内网安全? 其实就是网络安全的一种。 什么是内网安全软件? 内网安全软件是企业保障内网安全的一种重要工具。 它主要帮助企业实现对网络设备、应用程序、用户行为等方面的监控和管理,以预防和应对各种网络攻击。 这类软件主要用于对内网中…...
【摘葡萄game】
您想要了解的“摘葡萄游戏”可能是一个编程项目或者是一个编程相关的练习。我可以提供一个简单的摘葡萄游戏的思路和代码示例。这个游戏可以用多种编程语言来实现,比如Python、Java等。这里我以Python为例,给出一个基础版本的摘葡萄游戏的概念和代码。 …...
java如何实现字符串连接
在java中,字符串与字符串连接可以用运算符和 比如有字符串a,字符串b 想要把a和b连接起来,定义一个字符串变量c cab 或者 ab 示例代码 public class Zifuchuanlianjie {public static void main(String[] args) {String a"我叫李狗蛋";S…...
流量卡选卡攻略,拯救不会选流量卡的小白!
家人们,你们知道不,选择一款性价比高的流量卡,真的超级省钱。 一、首先,说一说申请。 运营商推出线上流量卡,注意是线上的流量卡,都是免费领取,运营商包邮到家,在激活充值之前不…...
python class __format__ __bytes__区别
在Python中,__format__和__bytes__是两个特殊方法,它们允许对象自定义它们在特定情境下的字符串表示。以下是这两个方法的区别和作用: __format__ 作用:__format__方法用于定义对象在使用format()函数或格式化字符串(…...
C++ | Leetcode C++题解之第134题加油站
题目: 题解: 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命令
这个命令主要是用于显示指定工作目录下之内容(列出目前工作目录所含的文件及子目录)。 掌握几个重点的常使用的就可以: ls -l # 以长格式显示当前目录中的文件和目录 ls -a # 显示当前目录中的所有文件和目录&am…...
多态、虚函数表与动态绑定的深入解析
目录 多态简介 虚函数表与动态绑定 虚函数表 动态绑定机制 内存与性能影响 纯虚函数与抽象类 纯虚函数 抽象类 动态类型转换与typeid操作符 dynamic_cast typeid操作符 虚析构函数的重要性 在面向对象编程中,多态性是一种核心特性,它允许我们…...
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(可选) Docker容器可以从底层虚拟化,使我们能够在 不降级 CUDA驱动程序的情况下使用 任何版本的CU…...
快速学习Java的多维数组技巧
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一…...
C语言运算类型有哪些
C语言中的运算类型主要分为以下几类: 1. 算术运算符: - 加法运算符 - 减法运算符 - - 乘法运算符 * - 除法运算符 / - 取模运算符 %(取余数) 2. 关系运算符: - 大于 > - 小于 < - 大…...
【深度学习】Loss为Nan的可能原因
文章目录 1. 问题情境2. 原因分析3. 导致Loss为Nan的其他可能原因 1. 问题情境 在某个网络架构下,我为某个数据项引入了一个损失函数。 这个数据项是nn.Embedding类型的,我加入的损失函数是对nn.Embedding空间做约束。 因为我在没加入优化loss前&#x…...
跨境直播进入“下半场”:2026年值得关注的几个新方向
很多人提到跨境直播,第一反应还是“流量”和“带货”。但如果这两年持续关注行业变化,会发现一个明显趋势:跨境直播正在从“内容竞争”转向“技术能力竞争”。尤其从2025年开始,行业越来越卷的不只是主播,而是整个直播…...
如何3分钟精准定位Windows热键冲突:Hotkey Detective深度技术解析
如何3分钟精准定位Windows热键冲突:Hotkey Detective深度技术解析 【免费下载链接】hotkey-detective A small program for investigating stolen key combinations under Windows 7 and later. 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-detective …...
你的群晖NAS性能过剩了吗?试试用它跑个万兆测速服务,榨干内网带宽
如何用群晖NAS搭建专业级内网测速平台:从硬件压榨到性能调优全指南 当你为家庭或工作室部署了万兆网络环境后,最令人抓狂的莫过于花了大价钱升级设备,却无法确认实际带宽是否达标。那些标榜"万兆兼容"的交换机、网卡和NASÿ…...
从开源哲学到工程实践:探索Uncomfortable-filagree112/OpenViking的代码美学
1. 项目概述:当开源遇上“不适”的优雅最近在GitHub上闲逛,发现了一个名字相当有意思的项目:Uncomfortable-filagree112/OpenViking。初看这个标题,一股强烈的反差感扑面而来——“Uncomfortable”(不适)、…...
浏览器插件实现AI提示词无缝集成:提升对话效率的工程实践
1. 项目概述与核心价值最近在折腾AI工具链的时候,发现了一个挺有意思的GitHub项目:fatihsolhan/prompts-chat-extension。乍一看名字,你可能会觉得这又是一个“提示词管理”或者“聊天增强”的浏览器插件,市面上这类工具已经多如牛…...
STM32H743 FDCAN实战:手把手教你调试CAN节点错误计数器与Bus_Off状态
STM32H743 FDCAN实战:从寄存器到代码的Bus_Off恢复指南 当你的STM32H743项目突然出现CAN通信中断,调试器里FDCAN_PSR寄存器的BOFF位亮起红灯时,真正的挑战才刚刚开始。这不是普通的通信故障,而是触发了CAN协议中最严厉的惩罚机制—…...
Prompt-Builder:构建可复用提示词模板,提升大模型工程化效率
1. 项目概述:Prompt-Builder 是什么,以及为什么你需要它如果你和我一样,在过去一年里深度使用过各种大语言模型,那你一定经历过这样的时刻:面对一个复杂的任务,你精心构思的提示词(Prompt&#…...
电路设计效率革命:Draw.io电子工程库的专业绘图方案
电路设计效率革命:Draw.io电子工程库的专业绘图方案 【免费下载链接】Draw-io-ECE Custom-made draw.io-shapes - in the form of an importable library - for drawing circuits and conceptual drawings in draw.io. 项目地址: https://gitcode.com/gh_mirrors/…...
热销榜单:2026年深圳App开发公司推荐,揪出大众推荐的五大高口碑产品
在2026年、深圳的App开发公司凭借其创新能力逐渐崭露头角。在这个市场中解决方案、从电商到物联网设计美学赢得了用户信任;而本凡码农科技则专注于小程序定制、满足市场对便捷应用的追求。还有、云码科技伴随着创新技术提供了更高等灵活性,而晨曦科技结合…...
【最新 v2.7.1 版本】5 分钟搞定 OpenClaw Windows 环境部署配置
OpenClaw(小龙虾)Windows 一键部署保姆级教程 | 10 分钟搭建专属数字员工【点击下载最新OpenClaw安装包】 前言 2026 年开源圈热门 AI 智能体 OpenClaw(昵称小龙虾),GitHub 星标突破 28 万,凭借本地运行 …...
