MyBatis大数据量插入方案

news2025/7/22 14:08:57

1、前言

在开发过程中,有时我们会碰到将大批量的数据入库的场景,那么我们一般有下面三种方式入库:

  1. ExecutorType.BATCH批处理方式插入
  2. foreach循环标签插入
  3. MyBatisPlus自带的saveBatch批量新增方法

下面我们用一个案例来测试一下,看下三种方式哪种效率最好

2、案例说明

现在我数据库新建一张表t_user,建表语句如下:

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手机号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = DYNAMIC;

我这里MySQL数据库版本是5.5.28

然后我用上面三种方式分别向t_user表中存5万条数据,每次存数据前,先清空一下表数据,使用下面语句快速清除表数据:

truncate table t_user;

每种方式我都测试5次,然后每种方式我都计算一个耗时平均值,看哪种方式耗时最小

3、编码

这是我的基础项目:https://gitee.com/colinWu_java/spring-boot-base.git

我会在此基础项目上做测试

3.1、ExecutorType.BATCH批处理方式插入

ExecutorType.BATCH介绍:

  1. Mybatis内置的ExecutorType有3种,SIMPLE、REUSE、BATCH,默认的是simple,该模式下它为每个语句的执行创建一个新的预处理语句,单条提交sql
  2. 而batch模式重复使用已经预处理的语句,并且批量执行所有更新语句,显然batch性能将更优;但batch模式也有自己的问题,比如在Insert操作时,在事务没有提交之前,是没有办法获取到自增的id,这在某型情形下是不符合业务要求的
  3. 通过批处理的方式,我们就可以在 JDBC 客户端缓存多条 SQL 语句,然后在 flush 或缓存满的时候,将多条 SQL 语句打包发送到数据库执行,这样就可以有效地降低上述两方面的损耗,从而提高系统性能

下面开始编码

在UserController新增下面接口

/**
     * 测试大批量数据插入数据库
     * 方式1:用一个 for 循环,把数据一条一条的插入
     * @return
     */
@GetMapping("/insertUser1/{count}")
public JSONResult insertUser1(@PathVariable("count") Integer count){
    return userService.insertUser1(count);
}

userService代码:

public JSONResult insertUser1(Integer count) {
    //如果自动提交设置为true,将无法控制提交的条数。所以我这里设置为false,,改为统一提交
    SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
    UserMapper uMapper = session.getMapper(UserMapper.class);
    //获取测试用户集合数据
    List<User> userList = Tools.getUserList(count);
    int commitCount = 5000;//每次提交的数量条数
    long startTime = System.currentTimeMillis();
    for(int i=0; i<userList.size(); i++){
        uMapper.addUserOne(userList.get(i));
        if (i != 0 && i % commitCount == 0) {
            session.commit();
        }
    }
    session.commit();
    long endTime = System.currentTimeMillis();
    log.info("方式1耗时:{}", (endTime - startTime));
    return JSONResult.success();
}

在UserMapper中新增addUserOne方法:

Integer addUserOne(User user);

对应xml代码:

<insert id="addUserOne">
    insert into t_user (name, age, phone) values (#{name}, #{age}, #{phone})
</insert>

getUserList方法代码如下,就是获取指定数量的测试用户数据而已:

/**
     * 获取指定数量的用户测试对象
     * @param count 数量
     * @return
     */
public static List<User> getUserList(int count){
    List<User> userList = new ArrayList<>();
    User user = null;
    for(int i=1; i<=count; i++){
        user = new User();
        user.setName("王天霸" + i + "号");
        user.setAge(i);
        user.setAge(i);
        user.setPhone("18022222222");
        userList.add(user);
    }
    return userList;
}

注意事项:

如果单次提交给MySQL数据库的数据量太大的话,可能会报如下错误:

com.mysql.jdbc.PacketTooBigException: Packet for query is too large (204444558 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
  • 出现这个错误的原因是:

大数据量的插入或更新会被 max_allowed_packet 参数限制,导致失败,因为MySQL根据配置文件会限制server允许接收的数据包大小

  • 解决方案:

修改MySQL的配置参数

打开Mysql命令客户端:

在这里插入图片描述

然后输入密码之后输入下面命令:

show VARIABLES like '%max_allowed_packet%';

结果:

在这里插入图片描述

用这个值除以1024*1024等于200M,那我这里就是设置的200M,根据实际情况设置该值,执行如下命令即可:

set global max_allowed_packet = 200*1024*1024;

设置完成之后,需要关闭MySQL服务,然后再重启(注意,这里最大设置为:1G

重启成功后,需要关掉上面的MySQL命令客户端,再重新打开一次,查看是否设置成功

注意:

  • MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入
  • MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组SQL语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能
  • 只有把rewriteBatchedStatements参数置为true,驱动才会帮你批量执行SQL,另外这个选项对INSERT/UPDATE/DELETE都有效
url: jdbc:mysql://127.0.0.1:3306/test1?allowMultiQueries=true&rewriteBatchedStatements=true
//allowMultiQueries=true,允许一次性执行多条SQL,批量插入时必须在连接地址后面加allowMultiQueries=true这个参数
//rewriteBatchedStatements=true,批量将数据传给MySQL,数据库会更高性能的执行批量处理,MySQL数据库版本在5.1.13以上,才能实现高性能的批量插入

3.2、foreach循环标签插入

UserController新增方法:

/**
     * 测试大批量数据插入数据库
     * 方式2:foreach标签批量插入
     * @return
     */
@GetMapping("/insertUser2/{count}")
public JSONResult insertUser2(@PathVariable("count") Integer count){
    return userService.insertUser2(count);
}

insertUser2代码:

public JSONResult insertUser2(Integer count) {
    //获取测试用户集合数据
    List<User> userList = Tools.getUserList(count);
    int countItem = 5000;//每次提交的记录条数
    int userSize = userList.size();
    List<User> userListTemp = new ArrayList<>();
    long startTime = System.currentTimeMillis();
    for (int i = 0, n=userSize; i < n; i++) {
        User user= userList.get(i);
        userListTemp.add(user);
        if ((i>0 && i % countItem == 0) || i == userSize - 1) {
            //每5000条记录提交一次
            userMapper.insertUserBatch(userListTemp);
            userListTemp.clear();
        }
    }
    long endTime = System.currentTimeMillis();
    log.info("方式2耗时:{}", (endTime - startTime));
    return JSONResult.success();
}

UserMapper的insertUserBatch方法:

void insertUserBatch(@Param("userList") List<User> userList);

对应xml代码:

<insert id="insertUserBatch">
    insert into t_user (name, age, phone) values
    <foreach collection="userList" item="user" separator=",">
        (#{user.name}, #{user.age}, #{user.phone})
    </foreach>
</insert>

3.3、MyBatisPlus自带的saveBatch批量新增方法

UserController新增方法:

@Autowired
public UserService userService;
/**
     * 测试大批量数据插入数据库
     * 方式3:mybatisplus自带的saveBatch批量新增方法
     * @return
     */
@GetMapping("/insertUser3/{count}")
public JSONResult insertUser3(@PathVariable("count") Integer count){
    //获取测试用户集合数据
    List<User> userList = Tools.getUserList(count);
    long startTime = System.currentTimeMillis();
    userService.saveBatch(userList, 5000);//每次往数据库提交5000条数据
    long endTime = System.currentTimeMillis();
    log.info("方式3耗时:{}", (endTime - startTime));
    return JSONResult.success();
}

4、测试

在浏览器中访问地址:http://localhost:8001/user/insertUser1/50000

这里的50000是表示插入5万条数据到t_user表

每次访问后,都记录一下耗时,然后清掉t_user表数据,再访问,反复测试5次

然后再访问http://localhost:8001/user/insertUser2/50000和http://localhost:8001/user/insertUser2/50000

步骤和上面一致,最终我统计出来的结果如下:

在这里插入图片描述

很明显,方式一效率最高
注意:以上结果仅是我本地测试情况,大家机器上可能会不同

5、总结

  1. 经过今天的测试,我们知道批量插入数据最快的方式就是ExecutorType.BATCH批处理方式插入
  2. 有些小的注意点记得在实际开发中谨慎处理,比如url参数配置,还有提交数量不要太大

如果本文对你有帮助的话,记得帮忙点个赞哦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17008.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

让学前端不再害怕英语单词(一)

有很多跟着我学习的学生经常跟我抱怨前端的单词很多&#xff0c;学了css又忘了html的单词&#xff0c;学了js又忘了css 的单词&#xff0c;所以本着给跟着我学习的学生提供一个学习前端不怕英语单词的课程&#xff0c;就打算写一篇博客去讲述如何增强在学习前端的时候巩固语法并…

通过阅读源码解决项目难题:GToken替换JWT实现SSO单点登录

文章目录jwt的问题jwt的请求流程图gtoken的优势注意问题演示demo入门示例运行效果启动项目&#xff1a;访问不认证接口&#xff1a;返回成功未登录时访问认证接口&#xff1a;返回错误提示请求登录接口&#xff1a;返回token携带token再次访问认证接口&#xff1a;返回成功分析…

postgres-operator 原理解析- 章节 I

这篇文章我想写postgres-operator如何利用kubernetes实现高可用功能其中的客户端流量路由部分。 总体的目的呢就是客户端数据库连接请求&#xff0c;如果通过利用kubernetes的机制实现将流量路由到实际的Postgresql主节点。 基础知识 Services without selectors 平常得Ser…

【Java进阶篇】第三章 常用类

文章目录一、String类1、String类概述2、String字符串的存储原理3、有String型属性的对象4、两种字符串对象创建方式的区别5、String类的特殊构造方法6、String类中的方法二、StringBuffer类1、StringBuffer类的构造方法2、String类和StringBuffer类的区别3、StringBuffer和Str…

我修复了一个 Vite Bug,让我的项目首屏性能提高了 25%

本文正在参加「金石计划 . 瓜分6万现金大奖」 一次偶然的机会&#xff0c;我将项目&#xff08;基于 tdesign-vue-next-starter &#xff09;由 Vite 2.7 升级成 Vite 3.x 后&#xff0c;发现首次运行 Vite dev 构建&#xff0c;页面首屏时间非常长&#xff0c;且一定会整个页…

Vue3基础看这一篇就够了(万字长篇,附实例代码及效果演示)

目录 前言 概述 Vue3组合式api VS Vue2选项式api 基础部分 setup 选项式api的风格 组合式api的风格 区别 响应式数据 ref reactive shallowReactive 与 shallowRef 计算属性和监听 computed 函数 watch 函数 watchEffect 生命周期 响应式数据只读 toRaw 返回代…

呼叫中心中间件(mod_cti基于FreeSWITCH)-背景音(彩话)接口

背景音&#xff0c;就是给通话添加一个背景音&#xff0c;比如办公室的噪音&#xff0c;键盘敲击声&#xff0c;等。彩话&#xff0c;就是通话过程播放一个声音&#xff0c;代替人工说话&#xff0c;这个声音双方可以同时听到&#xff0c;而且播放过程不影响双方通话。 用处 …

「JVS低代码开发平台」关于逻辑引擎的触发讲解

JVS逻辑引擎是代码开发套件中的业务瓶装的核心&#xff0c;用于去实现各种场景下的逻辑功能&#xff0c;可以把他理解为一个程序配置器与程序的执行器。 逻辑引擎是可以被多种配置器调用的触发的&#xff0c;从而实现了各种业务场景中对应功能的实现&#xff0c;那么接下来我们…

RabbitMQ初步到精通-第四章-RabbitMQ工作模式-PUB/SUB

第四章-RabbitMQ工作模式-PUB/SUB 1.模式介绍 1.1 模式 此模式称为发布订阅模式&#xff0c;从此模式开始&#xff0c;我们就不再使用默认的交换机了&#xff0c;开始定义我们自己的交换机。 此发布订阅模式&#xff0c;使用的交换机类型为Fanout。定义好交换机&#xff0c;消…

【MATLAB教程案例42】语音信号的MFCC特征提取matlab仿真

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 本课程学习成果预览: 目录 1.软件版本 2.MFCC理论概述

JavaScript之BOM复习(54th)

1、BOM概述 1、BOM Browser Object Model 浏览器对象模型 2、它提供了独立于内容而与浏览器窗口进行交互的对象&#xff0c;其核心对象是 window 3、BOM 由一系列相关的对象构成&#xff0c;并且每个对象都提供了很多方法与属性 4、BOM 缺乏标准&#xff0c;JavaScript 语法的…

用QT实现一个简单的桌面宠物

有时候桌面空空的&#xff0c;或者屏幕空旷了&#xff0c;我们就可以找一点东西来点缀一下&#xff0c;那么桌面宠物是一个不错的选择。 作为一个程序猿&#xff0c;如何实现一个桌面宠物呢&#xff1f; 本文就给大家带来的是如何用qt提供一种思路并写一个简单的桌面宠物。 思…

深入理解Linux网络技术内 幕(八)——设备注册和初始化

文章目录前言设备注册之时设备除名之时分配net_device结构NIC注册和除名的架构设备初始化设备驱动程序初始化设备类型初始化&#xff1a;xxx_setup函数可选的初始化和特殊情况net_device结构的组织查询设备状态队列规则状态注册状态设备的注册和除名切割操作&#xff1a;netdev…

C#编程的构成要素(结合unity做实例分析)

目录 定义变量 变量的名称很重要 将变量作为占位符 疯狂的方法 方法驱动行为 方法也是占位符 类的引入 一直在使用类 日常蓝图 注释是关键 将脚本附加到游戏对象上 脚本成为组件 类与组件通信 本文主要来自<<C#实践入门>>哈里森.费隆 著&#xff0c;仅用…

甘露糖-聚乙二醇-氨基|mannose-PEG-NH2|氨基-PEG-甘露糖

甘露糖-聚乙二醇-氨基|mannose-PEG-NH2|氨基-PEG-甘露糖 氨基&#xff08;Amino&#xff09;由一个氮原子和两个氢原子构成&#xff0c;化学式为-NH2。在有机化学中&#xff0c;氨基是基本碱基&#xff0c;大多数含有氨基的有机物都有一定碱的特性&#xff0c; 中文名称&…

基于数学形态学的路面裂缝图像处理技术-含Matlab代码

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、图像预处理✳️ 三、路面裂缝图像的边缘检测✳️ 3.1 裂缝识别✳️ 3.2 裂缝区域信息获取✳️ 3.3 裂缝特征提取✳️ 四、参考文献✳️ 五、Matlab代码获取✳️ 一、引言 对于路面裂缝而言&#xff0c; 采用图像处理技术对其进行识别与计…

③计算机病毒实验实验报告

班级 计科2101 姓名 彭彭头 学号 时间 2022年5月6日 成绩 实验项目名称 计算机病毒实验二 实验目的 1、了解脚本病毒的感染方式。 2、了解脚本病毒的手工清除方法。 实验内容 通过批处理文件进行计算机病毒和编写&#xff0c;了解脚本病毒的感染方式。 实验环…

Java笔记(十三)

文献种类&#xff1a;专题技术总结文献 开发工具与关键技术&#xff1a; IntelliJ IDEA、Java 语言 作者&#xff1a; 方建恒 年级&#xff1a; 2020 撰写时间&#xff1a; 2022 年 11 月 18 日 Java笔记(十三) 今天我给大家继续分享一下我的Java笔记&#xff0c; 我们继续来…

【Linux】环境基础开发工具使用

Vim Vim 是一个编辑器 只能编辑&#xff0c;只能写代码 直接输入vim &#xff1a; q就是退出 touch新文件&#xff0c;vim 进入 vim是一款多模式的编辑器 命令模式&#xff08;默认打开的模式&#xff09; 按 i 进入编辑模式/插入模式 esc回到命令模式 冒号进入底行…

【前沿技术RPA】 一文了解UiPath的代码审查工具Workflow Analyzer

&#x1f40b;作者简介&#xff1a;博主是一位.Net开发者&#xff0c;同时也是RPA和低代码平台的践行者。 &#x1f42c;个人主页&#xff1a;会敲键盘的肘子 &#x1f430;系列专栏&#xff1a;UiPath &#x1f980;专栏简介&#xff1a;UiPath在传统的RPA&#xff08;Robotic…