mysql 批量插入

article/2025/10/25 12:56:13

最近新的项目写了不少各种 insertBatch 的代码,一直有人说,批量插入比循环插入效率高很多,那本文就来实验一下,到底是不是真的?

测试环境:

  • SpringBoot 2.5
  • Mysql 8
  • JDK 8
  • Docker

首先,多条数据的插入,可选的方案:

  • foreach循环插入
  • 拼接sql,一次执行
  • 使用批处理功能插入

搭建测试环境`

sql文件:

drop database IF EXISTS test;
CREATE DATABASE test;
use test;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` int(11) NOT NULL,`name` varchar(255) DEFAULT "",`age` int(11) DEFAULT 0,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

应用的配置文件:

server:port: 8081
spring:#数据库连接配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&&serverTimezone=UTC&setUnicode=true&characterEncoding=utf8&&nullCatalogMeansCurrent=true&&autoReconnect=true&&allowMultiQueries=trueusername: rootpassword: 123456
#mybatis的相关配置
mybatis:#mapper配置文件mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.aphysia.spingbootdemo.model#开启驼峰命名configuration:map-underscore-to-camel-case: true
logging:level:root: error复制代码

启动文件,配置了Mapper文件扫描的路径:

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.aphysia.springdemo.mapper")
public class SpringdemoApplication {public static void main(String[] args) {SpringApplication.run(SpringdemoApplication.class, args);}}
复制代码

Mapper文件一共准备了几个方法,插入单个对象,删除所有对象,拼接插入多个对象:

import com.aphysia.springdemo.model.User;
import org.apache.ibatis.annotations.Param;import java.util.List;public interface UserMapper {int insertUser(User user);int deleteAllUsers();int insertBatch(@Param("users") List<User>users);
}
复制代码

Mapper.xml文件如下:

<?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.aphysia.springdemo.mapper.UserMapper"><insert id="insertUser" parameterType="com.aphysia.springdemo.model.User">insert  into user(id,age) values(#{id},#{age})</insert><delete id="deleteAllUsers">delete from user where id>0;</delete><insert id="insertBatch" parameterType="java.util.List">insert into user(id,age) VALUES<foreach collection="users" item="model" index="index" separator=",">(#{model.id}, #{model.age})</foreach></insert>
</mapper>
复制代码

测试的时候,每次操作我们都删除掉所有的数据,保证测试的客观,不受之前的数据影响。

不同的测试

1. foreach 插入

先获取列表,然后每一条数据都执行一次数据库操作,插入数据:

@SpringBootTest
@MapperScan("com.aphysia.springdemo.mapper")
class SpringdemoApplicationTests {@AutowiredSqlSessionFactory sqlSessionFactory;@ResourceUserMapper userMapper;static int num = 100000;static int id = 1;@Testvoid insertForEachTest() {List<User> users = getRandomUsers();long start = System.currentTimeMillis();for (int i = 0; i < users.size(); i++) {userMapper.insertUser(users.get(i));}long end = System.currentTimeMillis();System.out.println("time:" + (end - start));}
}
复制代码

2. 拼接sql插入

其实就是用以下的方式插入数据:

INSERT INTO `user` (`id`, `age`) 
VALUES (1, 11),
(2, 12),
(3, 13),
(4, 14),
(5, 15);
复制代码
    @Testvoid insertSplicingTest() {List<User> users = getRandomUsers();long start = System.currentTimeMillis();userMapper.insertBatch(users);long end = System.currentTimeMillis();System.out.println("time:" + (end - start));}
复制代码

3. 使用Batch批量插入

MyBatis sessionexecutor type 设为 Batch ,使用sqlSessionFactory将执行方式置为批量,自动提交置为false,全部插入之后,再一次性提交:

    @Testpublic void insertBatch(){SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = getRandomUsers();long start = System.currentTimeMillis();for(int i=0;i<users.size();i++){mapper.insertUser(users.get(i));}sqlSession.commit();sqlSession.close();long end = System.currentTimeMillis();System.out.println("time:" + (end - start));}
复制代码

4. 批量处理+分批提交

在批处理的基础上,每1000条数据,先提交一下,也就是分批提交。

    @Testpublic void insertBatchForEachTest(){SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = getRandomUsers();long start = System.currentTimeMillis();for(int i=0;i<users.size();i++){mapper.insertUser(users.get(i));if (i % 1000 == 0 || i == num - 1) {sqlSession.commit();sqlSession.clearCache();}}sqlSession.close();long end = System.currentTimeMillis();System.out.println("time:" + (end - start));}复制代码

初次结果,明显不对?

运行上面的代码,我们可以得到下面的结果,for循环插入的效率确实很差,拼接的sql效率相对高一点,看到有些资料说拼接sql可能会被mysql限制,但是我执行到1000w的时候,才看到堆内存溢出。

下面是不正确的结果!!!

插入方式1010010001w10w100w1000w
for循环插入3871150790770026635984太久了...太久了...
拼接sql插入308320392838315624948OutOfMemoryError: 堆内存溢出
批处理392917544251647470666太久了...太久了...
批处理 + 分批提交359893527550270472462太久了...太久了...

拼接sql并没有超过内存

我们看一下mysql的限制:

mysql> show VARIABLES like '%max_allowed_packet%';
+---------------------------+------------+
| Variable_name             | Value      |
+---------------------------+------------+
| max_allowed_packet        | 67108864   |
| mysqlx_max_allowed_packet | 67108864   |
| slave_max_allowed_packet  | 1073741824 |
+---------------------------+------------+
3 rows in set (0.12 sec)
复制代码

67108864足足600多M,太大了,怪不得不会报错,那我们去改改一下它吧,改完重新测试:

  1. 首先在启动mysql的情况下,进入容器内,也可以直接在Docker桌面版直接点Cli图标进入:
docker exec -it mysql bash
复制代码
  1. 进入/etc/mysql目录,去修改my.cnf文件:
cd /etc/mysql
复制代码
  1. 先按照vim,要不编辑不了文件:
apt-get update
apt-get install vim
复制代码
  1. 修改my.cnf
vim my.cnf
复制代码
  1. 在最后一行添加max_allowed_packet=20M(按i编辑,编辑完按esc,输入:wq退出)
 
[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
secure-file-priv= NULL
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0# Custom config should go here
!includedir /etc/mysql/conf.d/
max_allowed_packet=2M
复制代码
  1. 退出容器
# exit
复制代码
  1. 查看mysql容器id
docker ps -a
复制代码

image-20211130005909539

  1. 重启mysql
docker restart c178e8998e68
复制代码

重启成功后查看最大的max_allowed_pactet,发现已经修改成功:

mysql> show VARIABLES like '%max_allowed_packet%';
+---------------------------+------------+
| Variable_name             | Value      |
+---------------------------+------------+
| max_allowed_packet        | 2097152    |
| mysqlx_max_allowed_packet | 67108864   |
| slave_max_allowed_packet  | 1073741824 |
+---------------------------+------------+
复制代码

我们再次执行拼接sql,发现100w的时候,sql就达到了3.6M左右,超过了我们设置的2M,成功的演示抛出了错误:

org.springframework.dao.TransientDataAccessResourceException: 
### Cause: com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (36,788,583 > 2,097,152). You can change this value on the server by setting the 'max_allowed_packet' variable.
; Packet for query is too large (36,788,583 > 2,097,152). You can change this value on the server by setting the 'max_allowed_packet' variable.; nested exception is com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (36,788,583 > 2,097,152). You can change this value on the server by setting the 'max_allowed_packet' variable.复制代码

批量处理为什么这么慢?

但是,仔细一看就会发现,上面的方式,怎么批处理的时候,并没有展示出优势了,和for循环没有什么区别?这是对的么?

这肯定是不对的,从官方文档中,我们可以看到它会批量更新,不会每次去创建预处理语句,理论是更快的。

image-20211130011820487

然后我发现我的一个最重要的问题:数据库连接 URL 地址少了rewriteBatchedStatements=true

如果我们不写,MySQL JDBC 驱动在默认情况下会忽视 executeBatch() 语句,我们期望批量执行的一组 sql 语句拆散,但是执行的时候是一条一条地发给 MySQL 数据库,实际上是单条插入,直接造成较低的性能。我说怎么性能和循环去插入数据差不多。

只有将 rewriteBatchedStatements 参数置为 true, 数据库驱动才会帮我们批量执行 SQL

正确的数据库连接:

jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&&serverTimezone=UTC&setUnicode=true&characterEncoding=utf8&&nullCatalogMeansCurrent=true&&autoReconnect=true&&allowMultiQueries=true&&&rewriteBatchedStatements=true
复制代码

找到问题之后,我们重新测试批量测试,最终的结果如下:

插入方式1010010001w10w100w1000w
for循环插入3871150790770026635984太久了...太久了...
拼接sql插入308320392838315624948(很可能超过sql长度限制)OutOfMemoryError: 堆内存溢出
批处理(重点)33332336263616388978OutOfMemoryError: 堆内存溢出
批处理 + 分批提交359313394630290718631OutOfMemoryError: 堆内存溢出

从上面的结果来看,确实批处理是要快很多的,当数量级太大的时候,其实都会超过内存溢出的,批处理加上分批提交并没有变快,和批处理差不多,反而变慢了,提交太多次了,拼接sql的方案在数量比较少的时候其实和批处理相差不大,最差的方案就是for循环插入数据,这真的特别的耗时。100条的时候就已经需要1s了,不能选择这种方案。


作者:秦怀杂货店
链接:https://juejin.cn/post/7049143701590769678
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


http://chatgpt.dhexx.cn/article/qI7WMaN7.shtml

相关文章

Mysql批量插入1000条数据

使用mysql的存储过程 1.现有如下一张表&#xff1a;site_row 2.创建存储过程 CREATE PROCEDURE p01 () BEGIN declare i int; set i1;while i<1000 doINSERT INTO site_row(row_id,row_num) VALUES ( i,i);set ii1; end WHILE;END; 3.执行存储过程 CALL p01(); 4.查看效…

mysql 批量查询插入,MySQL批量插入查询出来的数据

timg.jpg 先看SQL&#xff1a; INSERT INTO movie_detail_tab (VIDEO_ID, PLAY_URL) SELECT b.ID AS VIDEO_ID, x.playurl AS PLAY_URL FROM ( SELECT a.playurl, CONCAT( a.卡通名, a.第几集 ) AS videoCname FROM ( SELECT playurl, SUBSTRING_INDEX(sname, , 1) AS 卡通名,…

C++向mysql批量插入数据

一、以下共统计了3种批量插入的方法: 1、普通方法:一条一条插入; 2、使用拼接语句一次性插入,拼接语句的语法如下: insert into table (col1,col2,col3) values (’a’,’b‘,’c‘), (’a1’,’b1‘,’c1‘),(’a2‘,’b2‘,’c2),...... 对于拼接语句sql有…

mysql新增,更新,批量插入操作

mysql新增和更新操作 一、Insert的几种语法 例子: CREATE TABLE users (id int(11) NOT NULL AUTO_INCREMENT,username varchar(30) DEFAULT NULL,money double DEFAULT NULL,PRIMARY KEY (id),UNIQUE KEY username_index (username) ) ENGINEInnoDB AUTO_INCREMENT10 DEFAU…

MySQL优化:批量插入大数据4种实用、讲究方案的测试

简明&#xff1a;本文记录个人使用MySQL插入大数据总结较实用的方案&#xff0c;通过对常用插入大数据的4种方式进行测试&#xff0c;即for循环单条、拼接SQL、批量插入saveBatch()、循环 开启批处理模式&#xff0c;得出比较实用的方案心得。 &#xff08;个人记录学习笔记内…

Hi3559A Hi3519A Hi3556A算力对比

Hi3559A Hi3519A Hi3556A算力对比 Hi3559A 即Hi3559ARFCV100 4T算力 封装 FC-BGA 常备 Hi3519A Hi3519ARFVV100 才是AI芯片 2T算力 订货 先进的智能IP摄像头Soc Hi3519V101 先进的工业IP摄像头Soc Hi3516DV300 1T算力 常备 专业4M智能IP摄像SoC Hi3516CV500 0.5T算力 常备货…

海思Hi3798MV310芯片处理器参数介绍

Hi3798MV310是用于IPTV/OTT机顶盒市场的支持4KP60 解码的超高清高性能SOC芯片。集成4核64位高性能Cortex A53处理器和多核高性能 2D/3D加速引擎&#xff1b;支持H.265/AVS2 4Kx2KP60 10bit 超高清视频解码&#xff0c;高性能的 H.265 高清视频编码&#xff0c;HDR视频解码及显示…

湖北电信黑盒创维E900V21E-HI3798MV310-MT7661RSN-当贝桌面-免拆卡刷固件包

湖北电信黑盒创维E900V21E-HI3798MV310-MT7661RSN-当贝桌面-免拆卡刷固件包-内有主板图及教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量…

移动机顶盒migu-jt-u1 unt400c刷机 hi3798 root

准备好u盘&#xff0c;把固件放进去&#xff0c;插到机顶盒上 1&#xff0c;打开机顶盒天灵盖。短接这两个敏感点&#xff0c;开机&#xff0c;不松手&#xff0c;等出现刷机界面。 视频 链接&#xff1a; caiyun.139.com/m/i?105Cq73pcbcbW 提取码&#xff1a;Bgq3 复制内容…

海思AI芯片(Hi3519A/3559A)方案学习(三)Ubuntu18.0.4上编译Hi3519AV100 uboot和kernel

先重申下我的平台为ubuntu18.0.4&#xff0c;SDK的目标平台为Hi3519AV100系列 准备工作 安装himix200交叉编译器 tar –xzf arm-himix200-linux.tgzsudo ./arm-himix200-linux.installsource /etc/profile 安装SDK包 tar -zxf Hi3519AV100_SDK_V2.0.1.0.tgzsudo ./sdk.unp…

Hi3798M V200 SDK文档介绍

目录 下载SDK并解压解压后主要的文件夹 下载SDK并解压 步骤1&#xff1a;下载Hi3798M V200 SDK。 大家如果有下载路径可以直接下载&#xff0c;如果没有的话可以使用我这个路径。 链接&#xff1a;https://pan.baidu.com/s/1buqwwZ7yBPNmi6JA2KG1eQ 提取码&#xff1a;dv6f ps…

FPGA+海思Hi3559

海思3559av100接收FPGA通过LVDS发送的模拟数据&#xff0c;&#xff0c;模拟数据为彩带。 FPGA可以模拟彩带或者黑白灰图像的模拟数据&#xff0c;通过LVDS协议&#xff08;LVDS为低电压差分信号&#xff0c;核心是采用极低的电压摆幅高速差动传输数据&#xff0c;可以实现点对…

海思Hi3798MV310机顶盒芯片Datasheet-基本信息

Hi3798M V310 是用于 IPTV/OTT 机顶盒市场的支持 4KP60 解码的超高清高性能 SOC芯片。集成 4 核 64 位高性能 Cortex A53 处理器和多核高性能 2D/3D 加速引擎&#xff1b;支持H.265/AVS2 4Kx2KP60 10bit 超高清视频解码&#xff0c;高性能的 H.265 高清视频编码&#xff0c;HDR…

海思Hi3798MV200机顶盒芯片规格书-基本信息

Hi3798MV200 是用于 IPTV/OTT 机顶盒市场的支持 4KP60 解码的全 4K 高性能 SOC芯片。集成 4 核 64 位高性能 Cortex A53 处理器和多核高性能 2D/3D 加速引擎;支持H.265 4Kx2K@P60 10bit 超高清视频解码,高性能的 H.265 高清视频编码,HDR 视频解码及显示,HDR 转 SDR,BT.202…

hi3798mv300是什么手机_海思Hi3798MV300/Hi3798MV310/Hi3798MV300H共升级包使用指南芯片手册...

本文针对现网各种场景&#xff0c;对hi3798MV300、Hi3798MV310、Hi3798MV300H共升级包方案的升级包的编译、制作方法和原理进行了详细介绍。 适用场景 场景1&#xff1a; 现网同时有Hi3798MV300和Hi3798MV300H&#xff0c;而且版本基线相同&#xff0c;现需要升级到最新版本 场…

Hi3519AV100 适配IMX347

前言 环境介绍&#xff1a; 1.编译环境 Ubuntu 18.04.5 LTS 2.SDK Hi3519AV100_SDK_V2.0.1.0 3.单板 Hi3519AV100开发板 IMX347 2688x1520(4M)30fps master mode i2c id 0x34 一、Sensor i2c寄存器读写 海思默认文件系统是有i2c读写工具的&#xff0c;可以使用这个工具…

海思Hi3798硬件设计,Hi3798 datasheet(2)参考资料

本文主要介绍 Hi3798C V200 芯片的硬件封装、管脚描述、管脚复用寄存器的配置方法、电气特性参数、原理图设计建议、PCB 设计建议、热设计建议等内容。本文主要为硬件工程师提供硬件设计的参考。 2.1 封装 Hi3798C V200 芯片 TFBGA&#xff08;Thin Fine BGA package&#xf…

HI3798MV200驱动移植

目录 1.UBOOT配置修改方法 2.由EMMC启动改为SPI NAND FLASH 启动 3.网络调试 4.PHY复位 5.内核起来网络不通 6.增加RTC 7.PHY 灯ACT LINK 问题 8.PHY link状态查询 9.ETH0 网络状态灯修改 1.UBOOT配置修改方法 需要对应版本的HITOOL&#xff0c;个人也是废了很大劲&a…

M301H-BYT代工-支持Hi3798 MV300H/MV300/MV310芯片-当贝纯净桌面-强刷卡刷固件包

M301H-BYT代工-支持Hi3798 MV300H&#xff0f;MV300&#xff0f;MV310芯片-当贝纯净桌面-强刷卡刷固件包 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff…

Hi3798 openSSH的移植

前言 在编译海思SDK时&#xff0c;会自动编译openssl&#xff0c;zlib&#xff0c;故无需重复编译这两者。 编译 编译openssh 本文下载openssh-7.3p1版本&#xff0c;将其下载至虚拟机 tar -xvf openssh-7.3p1.tar.gz ./configure --hostarm-linux --prefix/usr/local/ope…