SpringBoot中六种批量更新Mysql 方式效率对比
SpringBoot中六种批量更新Mysql 方式效率对比
先上结论吧,有空可以自测一下,数据量大时运行一次还时挺耗时的
效率比较
小数据量时6中批量更新效率不太明显,根据项目选择合适的即可,以1万条为准做个效率比较,效率从高到低一次排名如下
replace into和ON DUPLICATE KEY效率最高mybatis-plus有取巧嫌疑,因为是分批批量更新,其他几种都是一次更新- for循环凭借sql和JdbcTemplate相近,即使5万条,10万条效率也相近
- case when
然而有时候我们只能选择case when,因为replace into和ON DUPLICATE KEY公司不一定让用,项目也不一定引入mybatis-plus,数据库url中也不一定有allowMultiQueries=true参数,算是一个兜底方案吧,不管用那种方式大数据量时都需要考虑分批
测试结构
环境信息:mysql-8.0.35-winx64,本地win 10
依次为测试次数-平均耗时-最小耗时-最大耗时,单位为毫秒
| 数据量 | for | case when | replace into | ON DUPLICATE KEY | mybatis-plus | JdbcTemplate |
|---|---|---|---|---|---|---|
| 500 | 100-61-41-1202 | 100-66-57-426 | 100-16-10-282 | 100-15-10-293 | 100-73-52-564 | 100-87-59-1449 |
| 1000 | 100-131-94-2018 | 100-241-219-675 | 100-28-18-376 | 100-25-17-331 | 100-117-98-599 | 100-188-136-2397 |
| 5000 | 100-852-735-8297 | 100-11219-10365-13496 | 100-95-83-569 | 100-93-82-552 | 100-618-517-1415 | 100-1161-911-9334 |
| 10000 | 10-3957-2370-17304 | 10-45537-44465-48119 | 100-191-171-762 | 100-188-169-772 | 100-1309-1085-5021 | 100-3671-2563-31112 |
| 50000 | 10-50106-34568-130651 | 卡死不动 | 100-1026-919-1868 | 100-1062-945-1934 | 100-8062-6711-20841 | 100-48744-35482-191011 |
| 100000 | 10-160170-106223-264434 | 卡死不动 | 10-2551-2292-3688 | 10-2503-2173-3579 | 100-17205-14436-24881 | 10-169771-110522-343278 |
总结
-
sql语句for循环效率其实相当高的,因为它仅仅有一个循环体,只不过最后update语句比较多,量大了就有可能造成sql阻塞,同时在mysql的url上需要加上allowMultiQueries=true参数,即 jdbc:mysql://localhost:3306/mysqlTest?characterEncoding=utf-8&allowMultiQueries=true(公司项目不一定加,我们也不一定有权限加)。
-
case when虽然最后只会有一条更新语句,但是xml中的循环体有点多,每一个case when 都要循环一遍list集合,所以大批量拼sql的时候会比较慢,所以效率问题严重。使用的时候建议分批插入(我们公司一直用的就是这种,但是必须分批)。
-
duplicate key update可以看出来是最快的,但是公司一般都禁止使用replace into和INSERT INTO … ON DUPLICATE KEY UPDATE,这种sql有可能会造成数据丢失和主从上表的自增id值不一致。而且用这个更新时,记得一定要加上id,而且values()括号里面放的是数据库字段,不是java对象的属性字段
-
根据效率,安全方面综合考虑,选择适合的很重要。
数据库
CREATE TABLE `people` (`id` bigint(8) NOT NULL AUTO_INCREMENT,`first_name` varchar(50) NOT NULL DEFAULT '',`last_name` varchar(50) NOT NULL DEFAULT '',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
初始化测试数据
//初始化10w数据
@Test
void init10wData() {for (int i = 0; i < 100000; i++) {People people = new People();people.setFirstName(UUID.randomUUID().toString());people.setLastName(UUID.randomUUID().toString());peopleDAO.insert(people);}
}
批量修改方案
第一种 for
<!-- 批量更新第一种方法,通过接收传进来的参数list进行循环组装sql -->
<update id="updateBatch" parameterType="java.util.List"><foreach collection="list" item="item" index="index" open="" close="" separator=";">update people<set><if test="item.firstName != null">first_name = #{item.firstName,jdbcType=VARCHAR},</if><if test="item.lastName != null">last_name = #{item.lastName,jdbcType=VARCHAR},</if></set>where id = #{item.id,jdbcType=BIGINT}</foreach>
</update>
第二种 case when
<!-- 批量更新第二种方法,通过 case when语句变相的进行批量更新 -->
<update id="updateBatch2" parameterType="java.util.List">update people<set><foreach collection="list" item="item"><if test="item.firstName != null">first_name = case when id = #{item.id} then #{item.firstName} else first_name end,</if><if test="item.lastName != null">last_name = case when id = #{item.id} then #{item.lastName} else last_name end,</if></foreach></set>where id in<foreach collection="list" item="item" separator="," open="(" close=")">#{item.id}</foreach>
</update>
第三种 replace into
<!-- 批量更新第三种方法,通过 replace into -->
<update id="updateBatch3" parameterType="java.util.List">replace into people(id,first_name,last_name) values<foreach collection="list" index="index" item="item" separator=",">(#{item.id},#{item.firstName},#{item.lastName})</foreach>
</update>
第四种 ON DUPLICATE KEY UPDATE
<!-- 批量更新第四种方法,通过 duplicate key update -->
<update id="updateBatch4" parameterType="java.util.List">insert into people(id,first_name,last_name) values<foreach collection="list" index="index" item="item" separator=",">(#{item.id},#{item.firstName},#{item.lastName})</foreach>ON DUPLICATE KEY UPDATEid=values(id),first_name=values(first_name),last_name=values(last_name)
</update>
第五种mybatis-plus提供的的批量更新
default boolean updateBatchById(Collection<T> entityList) {return this.updateBatchById(entityList, 1000);
}
boolean updateBatchById(Collection<T> entityList, int batchSize);
mybatis-plus提供的批量更新是分批批量更新,默认每批1000条,可以指定分批的条数,每批执行完成后提交一下事务,不加@Transactional可能会出现第一批更新成功了,第二批更新失败了的情况.
第六种JdbcTemplate提供的批量更新
测试代码
/*** PeopleDAO继承基类*/
@Mapper
@Repository
public interface PeopleDAO extends MyBatisBaseDao<People, Long> {void updateBatch(@Param("list") List<People> list);void updateBatch2(List<People> list);void updateBatch3(List<People> list);void updateBatch4(List<People> list);
}
@SpringBootTest
class PeopleMapperTest {@ResourcePeopleMapper peopleMapper;@ResourcePeopleService peopleService;@ResourceJdbcTemplate jdbcTemplate;@Testvoid init10wData() {for (int i = 0; i < 100000; i++) {People people = new People();people.setFirstName(UUID.randomUUID().toString());people.setLastName(UUID.randomUUID().toString());peopleMapper.insert(people);}}@Testvoid updateBatch() {List<People> list = new ArrayList();int loop = 100;int count = 5000;Long maxCost = 0L;//最长耗时Long minCost = Long.valueOf(Integer.MAX_VALUE);//最短耗时for (int j = 0; j < count; j++) {People people = new People();people.setId(ThreadLocalRandom.current().nextInt(0, 100000));people.setFirstName(UUID.randomUUID().toString());people.setLastName(UUID.randomUUID().toString());list.add(people);}Long startTime = System.currentTimeMillis();for (int i = 0; i < loop; i++) {Long curStartTime = System.currentTimeMillis();// peopleMapper.updateBatch4(list);// peopleService.updateBatchById(list);jdbcTemplateBatchUpdate(list);Long curCostTime = System.currentTimeMillis() - curStartTime;if (maxCost < curCostTime) {maxCost = curCostTime;}if (minCost > curCostTime) {minCost = curCostTime;}}System.out.println(loop + "-" + (System.currentTimeMillis() - startTime) / loop + "-" + minCost + "-" + maxCost );}private void jdbcTemplateBatchUpdate (List<People> list){String sql = "update people set first_name=?,last_name=? where id = ?";List<Object[]> params = list.stream().map(item -> new Object[]{item.getFirstName(), item.getLastName(), item.getId()}).collect(Collectors.toList());jdbcTemplate.batchUpdate(sql,params);}
}
参考文章:
mybatis批量更新数据三种方法效率对比 https://blog.csdn.net/q957967519/article/details/88669552
MySql中4种批量更新的方法 https://blog.csdn.net/weixin_42290280/article/details/89384741
Mysql 批量修改四种方式效率对比(一)https://blog.csdn.net/zk673820543/article/details/106579809/
相关文章:
SpringBoot中六种批量更新Mysql 方式效率对比
SpringBoot中六种批量更新Mysql 方式效率对比 先上结论吧,有空可以自测一下,数据量大时运行一次还时挺耗时的 效率比较 小数据量时6中批量更新效率不太明显,根据项目选择合适的即可,以1万条为准做个效率比较,效率从高到低一次排名如下 replace into和ON DUPLICATE KEY效率最…...
【SpringBoot】SpringBoot整合jasypt进行重要数据加密
📝个人主页:哈__ 期待您的关注 目录 📕jasypt简介 🔥SpringBoot使用jasypt 📂创建我需要的数据库文件 📕引入依赖 🔓配置数据库文件(先不进行加密) 🌙创…...
【Go语言入门学习笔记】Part1.梦开始的地方
一、前言 经过一系列的学习,终于有时间来学习一些新的语言,Go语言在现在还是比较时髦的,多一个技能总比不多的好,故有时间来学一下。 二、配置环境 按照网络中已有的配置方法配置好,本人采用了Jetbrain的Goland&#…...
数据特征降维 | 主成分分析(PCA)附Python代码
主成分分析(Principal Component Analysis,PCA)是一种常用的数据降维技术和探索性数据分析方法,用于从高维数据中提取出最重要的特征并进行可视化。 PCA的基本思想是通过线性变换将原始数据投影到新的坐标系上,使得投影后的数据具有最大的方差。这些新的坐标轴称为主成分…...
当服务实例出现故障时,Nacos如何处理?
当服务实例出现故障时,Nacos的应对策略 在微服务架构日益盛行的今天,服务之间的稳定性与可靠性成为了我们架构师们不得不面对的重要课题。尤其是在面对服务实例出现故障时,如何确保整个系统的稳定运行,成为了我们首要考虑的问题。…...
遥感数据集制作(Potsdam数据集为例):TIF图像转JPG,TIF标签转PNG,图像重叠裁剪
文章目录 TIF图像转JPGTIF标签转PNG图像重叠裁剪图像重命名数据集转COCO格式数据集转VOC格式 遥感图像不同于一般的自然图像,由于波段数量、图像位深度等原因,TIF图像数据不能使用简单的格式转换方法。本文以Potsdam数据集为例,制作能够直接用…...
根据web访问日志,封禁请求量异常的IP,如IP在半小 时后恢复正常则解除封禁
在网络安全日益受到重视的今天,如何有效防范恶意流量和攻击成为了每个网站管理员必须面对的问题。恶意流量不仅会影响网站的正常运行,还可能导致服务器崩溃,给网站带来不可估量的损失。为了应对这一问题,我们特别推出了一款实用的…...
2.go语言初始(二)
本篇博客涉及到go 的基础数据类型、 go 语言中的运算符、转义字符、格式化输出、字符串操作 go 语言中的运算符 在 go 语言中,基本数据类型主要包括以下几类:整数类型、浮点数类型、复数类型、布尔类型、字符串类型、字节类型(byte…...
MQTT对比HTTP
吞吐量:根据3G网络的测量结果,MQTT的吞吐量比HTTP快93倍。这意味着在相同的网络条件下,MQTT能够更有效地传输数据,从而在处理大量数据或实时数据传输时具有更高的效率。架构与模式:MQTT基于发布/订阅模型,提…...
暴力数据结构之二叉树(堆的相关知识)
1. 堆的基本了解 堆(heap)是计算机科学中一种特殊的数据结构,通常被视为一个完全二叉树,并且可以用数组来存储。堆的主要应用是在一组变化频繁(增删查改的频率较高)的数据集中查找最值。堆分为大根堆和小根…...
死锁调试技巧:工作线程和用户界面线程
有人碰到了一个死锁问题,找到我们想请我们看看,这个是关于应用程序用户界面相关的死锁问题。 我也不清楚他为什么会找上我们,可能是因为我们经常会和窗口管理器打交道吧。 下面,我们来看看死锁的两个线程。 >> 请移步至 …...
蓝桥杯-外卖店优先级(简单写法)
“饱了么”外卖系统中维护着 N 家外卖店,编号 1∼N。 每家外卖店都有一个优先级,初始时 (0 时刻) 优先级都为 0。 每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1,最低减到 0;而如果外卖店有订…...
VueRouter使用总结
VueRouter 是 Vue.js 的官方路由管理器,用于构建单页面应用(SPA)。在使用 VueRouter 时,开发者可以定义路由映射规则,并在 Vue 组件中通过编程式导航或声明式导航的方式控制页面的跳转和展示。以下是 VueRouter 使用的…...
Flink checkpoint 源码分析- Checkpoint snapshot 处理流程
背景 在上一篇博客中我们分析了代码中barrier的是如何流动传递的。Flink checkpoint 源码分析- Checkpoint barrier 传递源码分析-CSDN博客 最后跟踪到了代码org.apache.flink.streaming.runtime.io.checkpointing.CheckpointedInputGate#handleEvent 现在我们接着跟踪相应…...
Leaflet.canvaslabel在Ajax异步请求时bindPopup无效的解决办法
目录 前言 一、场景重现 1、遇到问题的代码 2、问题排查 二、通过实验验证猜想 1、排查LayerGroup和FeatureGroup 2、排查Leaflet.canvaslabel.js 三、柳暗花明又一村 1、点聚类的办法 2、歪打正着 总结 前言 在上一篇博客中介绍了基于SpringBoot的全国风景区WebGIS按…...
Go 处理错误
如果你习惯了 try catch 这样的语法后,会觉得处理错误真简单,然后你再来接触 Go 的错误异常,你会发现他好复杂啊,怎么到处都是 error,到处都需要处理 error。 首先咱们需要知道 Go 语言里面有个约定,就是一…...
python读取excel数据写入mysql
概述 业务中有时会需要解析excel中的数据,按照要求处理后,写入到db中; 用python处理这个正好简便快捷 demo 没有依赖就 pip install pymysql一下 import pymysql from pymysql.converters import escape_string from openpyxl import loa…...
flutter日期选择器仅选择年、月
引入包:flutter_datetime_picker: 1.5.0 封装 import package:flutter/cupertino.dart; import package:flutter/material.dart; import package:flutter_datetime_picker/flutter_datetime_picker.dart;class ATuiDateTimePicker {static Future<DateTime> …...
素数筛详解c++
一、埃式筛法 代码 二、线性筛法(欧拉筛法) 主要的思想就是一个质数的倍数(倍数为1除外)肯定是合数,那么我们利用这个质数算出合数,然后划掉这个合数,下次就可以不用判断它是不是质数,节省了大量的时间。 …...
【Python超详细的学习笔记】Python超详细的学习笔记,涉及多个领域,是个很不错的笔记
获取笔记链接 Python超详细的学习笔记 一,逆向加密模块 1,Python中运行JS代码 1.1 解决中文乱码或者报错问题 import subprocess from functools import partial subprocess.Popen partial(subprocess.Popen, encodingutf-8) import execjs1.2 常用…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
