案例 百万数据批量插入测试

news/2025/2/13 2:30:36/文章来源:https://www.cnblogs.com/huakaiyoushi/p/18712364

1、jpa、mybatis-plus、jdbc、load data infile测试比较

2、load data infile语法测试

3、相关代码

测试

package com.xm;import com.xm.entity.UserP;
import com.xm.task.JdbcInsert2Task;
import com.xm.task.JdbcInsertTask;
import com.xm.task.MybatisPlusInsert2Task;
import com.xm.task.MybatisPlusInsertTask;
import com.xm.util.CommonUtil;
import com.xm.util.DataSourceUtil;
import com.xm.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;/*** 串行批量插入*/
@Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ThreadTests {/*** 并发线程池*/protected static ExecutorService executorService;static {executorService = Executors.newFixedThreadPool(10);}@Autowiredprivate SqlSessionFactory sqlSessionFactory;private final int[] totalArr = { 10000, 50000, 100000, 300000, 500000, 1000000 };/*** 总计数(更新下标进行批量测试)*/private final int total = totalArr[5];/*** 单批次最大计数(误解分区,单批次固定执行1000条数据,而不应该在线程中再拆分批次 这样占用的连接并不会及时释放)*/private final int batchSize = 50000;/*** 测试总次数(测试总条数 = total * count;耗时ms = 总耗时 / count)*/private final int count = 10;//插入的总条数增加,插入速度将会越来越慢(测试平均值:先清表再测试)//jdbc sql内置拼接,串行批量插入10000条数据结束。result=true,耗时0.2138s//jdbc sql内置拼接,串行批量插入50000条数据结束。result=true,耗时0.5108s//jdbc sql内置拼接,串行批量插入100000条数据结束。result=true,耗时0.8939s//jdbc sql内置拼接,串行批量插入300000条数据结束。result=true,耗时2.7112s//jdbc sql内置拼接,串行批量插入500000条数据结束。result=true,耗时5.1734s//jdbc sql内置拼接,串行批量插入1000000条数据结束。result=true,耗时9.6879s//jdbc sql内置拼接,串行批量插入500000条数据结束。result=true,耗时6204/5892/5811/5806/5861/5712/6360/6477/5864/6108ms,平均耗时:6009.5,单批次最大计数10000,单批次批量提交1000//jdbc sql内置拼接,串行批量插入500000条数据结束。result=true,耗时6072ms,单批次最大计数50000,单批次批量提交1000//jdbc sql内置拼接,串行批量插入500000条数据结束。result=true,耗时7786ms,单批次最大计数100000,单批次批量提交1000//jdbc sql内置拼接,串行批量插入500000条数据结束。result=true,耗时5880/5978/5654/5780/6067/5814/5814/5662/6100/6118ms,平均耗时:5886.7,单批次最大计数10000,单批次批量提交5000//jdbc sql内置拼接,串行批量插入500000条数据结束。result=true,耗时6179ms,单批次最大计数50000,单批次批量提交5000//jdbc sql内置拼接,串行批量插入500000条数据结束。result=true,耗时7960ms,单批次最大计数100000,单批次批量提交5000/*** jdbc(sql内置拼接)*/@Testvoid jdbc() throws InterruptedException {long start = System.currentTimeMillis();long other = 0;try {for (int i = 0; i < count; i++) {long begin = System.currentTimeMillis();List<UserP> users = CommonUtil.getUsers2(total, i, "jdbc sql内置拼接");other += System.currentTimeMillis() - begin;int commitSize = DataSourceUtil.getCommitSize();List<List<?>> chunks = StringUtil.groupBatchSize(users, commitSize);List<Future<String>> futures = new ArrayList<>();for (List<?> userList : chunks) {Future<String> future = (Future<String>) executorService.submit(new JdbcInsertTask((List<UserP>) userList));futures.add(future);}//查询总记录条数为0
                CommonUtil.waitFutures(futures);//查询总记录条数为total,表示执行结束,并立即清空表;为下次重置执行环境
//                long totalCount = this.count();
//                log.info("总记录数={}", totalCount);begin = System.currentTimeMillis();CommonUtil.clearTable();other += System.currentTimeMillis() - begin;}} catch (Exception e) {log.error("jdbc,串行批量插入数据异常!e={}", e.getMessage());throw new RuntimeException(e);} finally {long time = System.currentTimeMillis() - start - other;executorService.shutdown();boolean result = executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);long time0 = DataSourceUtil.atomicLong.get();String before = StringUtil.formatDecimal((double) time0 / 1000L, 4, RoundingMode.HALF_UP);String after = StringUtil.formatDecimal((double) time / count / 1000L, 4, RoundingMode.HALF_UP);log.info("jdbc,串行批量插入{}*{}条数据结束。result={},原耗时{}s,现耗时{}s", total, count, result, before, after);}}//jdbc2 sql手动拼接,串行批量插入10000条数据结束。result=true,耗时0.1968s//jdbc2 sql手动拼接,串行批量插入50000条数据结束。result=true,耗时0.5352s//jdbc2 sql手动拼接,串行批量插入100000条数据结束。result=true,耗时0.9192s//jdbc2 sql手动拼接,串行批量插入300000条数据结束。result=true,耗时2.3785s//jdbc2 sql手动拼接,串行批量插入500000条数据结束。result=true,耗时4.7409s//jdbc2 sql手动拼接,串行批量插入1000000条数据结束。result=true,耗时9.2848s/*** jdbc(sql手动拼接)*/@Testvoid jdbc2() throws InterruptedException {long start = System.currentTimeMillis();long other = 0;try {for (int i = 0; i < count; i++) {long begin = System.currentTimeMillis();List<UserP> users = CommonUtil.getUsers2(total, i, "jdbc2 sql手动拼接");other += System.currentTimeMillis() - begin;int commitSize = DataSourceUtil.getCommitSize();List<List<?>> chunks = StringUtil.groupBatchSize(users, commitSize);List<Future<String>> futures = new ArrayList<>();for (List<?> userList : chunks) {Future<String> future = (Future<String>) executorService.submit(new JdbcInsert2Task((List<UserP>) userList));futures.add(future);}CommonUtil.waitFutures(futures);begin = System.currentTimeMillis();CommonUtil.clearTable();other += System.currentTimeMillis() - begin;}} catch (Exception e) {log.error("jdbc2,串行批量插入数据异常!e={}", e.getMessage());throw new RuntimeException(e);} finally {long time = System.currentTimeMillis() - start - other;executorService.shutdown();boolean result = executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);long time0 = DataSourceUtil.atomicLong.get();String before = StringUtil.formatDecimal((double) time0 / 1000L, 4, RoundingMode.HALF_UP);String after = StringUtil.formatDecimal((double) time / count / 1000L, 4, RoundingMode.HALF_UP);log.info("jdbc2,串行批量插入{}*{}条数据结束。result={},原耗时{}s,现耗时{}s", total, count, result, before, after);}}//mybatis-plus 逐个insert,串行批量插入10000条数据结束。result=true,耗时0.2984s//mybatis-plus 逐个insert,串行批量插入50000条数据结束。result=true,耗时0.7840s//mybatis-plus 逐个insert,串行批量插入100000条数据结束。result=true,耗时1.6001s//mybatis-plus 逐个insert,串行批量插入300000条数据结束。result=true,耗时4.0869s//mybatis-plus 逐个insert,串行批量插入500000条数据结束。result=true,耗时6.5886s//mybatis-plus 逐个insert,串行批量插入1000000条数据结束。result=true,耗时13.1609s/*** mybatis-plus(逐个insert)*/@Testvoid mybatis_plus() throws InterruptedException {long start = System.currentTimeMillis();long other = 0;try {for (int i = 0; i < count; i++) {long begin = System.currentTimeMillis();List<UserP> users = CommonUtil.getUsers2(total, i, "mybatis-plus 逐个insert");other += System.currentTimeMillis() - begin;int commitSize = DataSourceUtil.getCommitSize();List<List<?>> chunks = StringUtil.groupBatchSize(users, commitSize);List<Future<String>> futures = new ArrayList<>();for (List<?> userList : chunks) {Future<String> future = (Future<String>) executorService.submit(new MybatisPlusInsertTask((List<UserP>) userList, sqlSessionFactory));futures.add(future);}CommonUtil.waitFutures(futures);begin = System.currentTimeMillis();CommonUtil.clearTable();other += System.currentTimeMillis() - begin;}} catch (Exception e) {log.error("mybatis-plus,串行批量插入数据异常!e={}", e.getMessage());throw new RuntimeException(e);} finally {long time = System.currentTimeMillis() - start - other;executorService.shutdown();boolean result = executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);long time0 = DataSourceUtil.atomicLong.get();String before = StringUtil.formatDecimal((double) time0 / 1000L, 4, RoundingMode.HALF_UP);String after = StringUtil.formatDecimal((double) time / count / 1000L, 4, RoundingMode.HALF_UP);log.info("mybatis-plus,串行批量插入{}*{}条数据结束。result={},原耗时{}s,现耗时{}s", total, count, result, before, after);}}//mybatis-plus2 批量batchAdd,串行批量插入10000条数据结束。result=true,耗时0.6944s//mybatis-plus2 批量batchAdd,串行批量插入50000条数据结束。result=true,耗时1.1751s//mybatis-plus2 批量batchAdd,串行批量插入100000条数据结束。result=true,耗时2.0128s//mybatis-plus2 批量batchAdd,串行批量插入300000条数据结束。result=true,耗时5.6418s//mybatis-plus2 批量batchAdd,串行批量插入500000条数据结束。result=true,耗时8.6080s//mybatis-plus2 批量batchAdd,串行批量插入1000000条数据结束。result=true,耗时16.4599s/*** mybatis-plus(批量batchAdd)*/@Testvoid mybatis_plus2() throws InterruptedException {long start = System.currentTimeMillis();long other = 0;try {for (int i = 0; i < count; i++) {long begin = System.currentTimeMillis();List<UserP> users = CommonUtil.getUsers2(total, i, "mybatis-plus2 批量batchAdd");other += System.currentTimeMillis() - begin;int commitSize = DataSourceUtil.getCommitSize();List<List<?>> chunks = StringUtil.groupBatchSize(users, commitSize);List<Future<String>> futures = new ArrayList<>();for (List<?> userList : chunks) {Future<String> future = (Future<String>) executorService.submit(new MybatisPlusInsert2Task((List<UserP>) userList, sqlSessionFactory));futures.add(future);}CommonUtil.waitFutures(futures);begin = System.currentTimeMillis();CommonUtil.clearTable();other += System.currentTimeMillis() - begin;}} catch (Exception e) {log.error("mybatis-plus2,串行批量插入数据异常!e={}", e.getMessage());throw new RuntimeException(e);} finally {long time = System.currentTimeMillis() - start - other;executorService.shutdown();boolean result = executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);long time0 = DataSourceUtil.atomicLong.get();String before = StringUtil.formatDecimal((double) time0 / 1000L, 4, RoundingMode.HALF_UP);String after = StringUtil.formatDecimal((double) time / count / 1000L, 4, RoundingMode.HALF_UP);log.info("mybatis-plus2,串行批量插入{}*{}条数据结束。result={},原耗时{}s,现耗时{}s", total, count, result, before, after);}}
}

线程任务JdbcInsertTask

package com.xm.task;
import com.xm.entity.UserP;
import com.xm.util.DataSourceUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
/*** jdbc(sql内置拼接) 批量插入数据任务*/
@Slf4j
public class JdbcInsertTask implements Runnable {private final List<UserP> users;public JdbcInsertTask(List<UserP> users) {this.users = users;}@Overridepublic void run() {if(!CollectionUtils.isEmpty(users)) {long start = System.currentTimeMillis();//1、注册驱动//2、创建数据库连接
//            long start2 = System.currentTimeMillis();
//            conn = DataSourceUtil.getHikariConnection();
//            conn = DataSourceUtil.getC3p0Connection();
//            log.info("从数据库连接池获取连接,耗时{}ms", System.currentTimeMillis() - start2);try (Connection conn = DataSourceUtil.getHikariConnection();) {//3、开启事务/取消事务自动提交conn.setAutoCommit(false);//4、执行SQL 获取结果集String sql = "INSERT INTO `user` " +"(`uuid`, `name`, `height`, `weight`, `age`, `bool`, `signature`, `delMark`, `createTime`, `updateTime`, `long_field`) " +"VALUES " +"(?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)";try (PreparedStatement ps = conn.prepareStatement(sql)) {for (UserP user : users) {ps.setString(1, user.getUuid());ps.setString(2, user.getName());ps.setBigDecimal(3, user.getHeight());ps.setBigDecimal(4, user.getWeight());ps.setInt(5, user.getAge());ps.setBoolean(6, user.getBool());ps.setString(7, user.getSignature());ps.setString(8, user.getCreateTimeStr());ps.setString(9, user.getUpdateTimeStr());ps.setString(10, user.getLongField());ps.addBatch();}ps.executeBatch();conn.commit();ps.clearBatch();}} catch (SQLException e) {//5、关闭连接(try-with-resources 异常或结束后会自动关闭资源)log.error("JdbcInsertTask 批量插入数据异常!e={}", e.getMessage());throw new RuntimeException(e);} finally {int total = users.size();long time = System.currentTimeMillis() - start;long totalTime = DataSourceUtil.atomicLong.addAndGet(time);log.info("JdbcInsertTask 批量插入{}条数据,耗时{}ms,totalTime={}", total, time, totalTime);}}}
}

线程任务JdbcInsert2Task

package com.xm.task;
import com.xm.entity.UserP;
import com.xm.util.DataSourceUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/*** jdbc(sql手动拼接) 批量插入数据任务*/
@Slf4j
public class JdbcInsert2Task implements Runnable {private final List<UserP> users;public JdbcInsert2Task(List<UserP> users) {this.users = users;}@Overridepublic void run() {if(!CollectionUtils.isEmpty(users)) {long start = System.currentTimeMillis();//1、注册驱动//2、创建数据库连接try (Connection conn = DataSourceUtil.getHikariConnection()) {//3、开启事务/取消事务自动提交conn.setAutoCommit(false);//4、执行SQL 获取结果集String sql = "INSERT INTO `user` " +"(`uuid`, `name`, `height`, `weight`, `age`, `bool`, `signature`, `delMark`, `createTime`, `updateTime`, `long_field`) " +"VALUES ";try (Statement statement = conn.createStatement()) {StringBuilder sqlBuilder = new StringBuilder(sql);for (UserP user : users) {sqlBuilder.append("(");sqlBuilder.append("'").append(user.getUuid()).append("'").append(", ").append("'").append(user.getName()).append("'").append(",").append(user.getHeight()).append(",").append(user.getWeight()).append(",").append(user.getAge()).append(",").append(user.getBool() ? 1 : 0).append(",").append("'").append(user.getSignature()).append("'").append(",").append(user.getDelMark()).append(",").append("'").append(user.getCreateTimeStr()).append("'").append(",").append("'").append(user.getUpdateTimeStr()).append("'").append(",").append("'").append("Hello jdbc!").append("'");sqlBuilder.append("),");}sqlBuilder.deleteCharAt(sqlBuilder.toString().length() - 1);statement.executeUpdate(sqlBuilder.toString());//一次性提交事务
                    conn.commit();}} catch (SQLException e) {//5、关闭连接(try-with-resources 异常或结束后会自动关闭资源)log.error("JdbcInsert2Task 批量插入数据异常!e={}", e.getMessage());throw new RuntimeException(e);} finally {int total = users.size();long time = System.currentTimeMillis() - start;long totalTime = DataSourceUtil.atomicLong.addAndGet(time);log.info("JdbcInsert2Task 批量插入{}条数据,耗时{}ms,totalTime={}", total, time, totalTime);}}}
}

线程任务MybatisPlusInsertTask

package com.xm.task;
import com.xm.entity.UserP;
import com.xm.mapper.UserMapper;
import com.xm.util.DataSourceUtil;
import com.xm.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.util.CollectionUtils;
import java.util.List;
/*** mybatis-plus(逐个insert) 批量插入数据任务*/
@Slf4j
public class MybatisPlusInsertTask implements Runnable {private final List<UserP> users;private final SqlSessionFactory sqlSessionFactory;public MybatisPlusInsertTask(List<UserP> users, SqlSessionFactory sqlSessionFactory) {this.users = users;this.sqlSessionFactory = sqlSessionFactory;}@Overridepublic void run() {if(!CollectionUtils.isEmpty(users)) {long start = System.currentTimeMillis();int total = users.size();//批量插入userstry (SqlSession sqlSession = this.sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {UserMapper userMapper = sqlSession.getMapper(UserMapper.class);for (UserP user : users) {userMapper.insert(user);}sqlSession.commit();} catch (Exception e) {log.error("MybatisPlusInsertTask 批量插入数据异常!e={}", e.getMessage());throw new RuntimeException(e);} finally {long time = System.currentTimeMillis() - start;long totalTime = DataSourceUtil.atomicLong.addAndGet(time);log.info("MybatisPlusInsertTask 批量插入{}条数据,耗时{}ms,totalTime={}", total, time, totalTime);}}}
}

线程任务MybatisPlusInsert2Task

package com.xm.task;
import com.xm.entity.UserP;
import com.xm.mapper.UserMapper;
import com.xm.util.DataSourceUtil;
import com.xm.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.util.CollectionUtils;
import java.util.List;
/*** mybatis-plus(批量batchAdd) 批量插入数据任务*/
@Slf4j
public class MybatisPlusInsert2Task implements Runnable {private final List<UserP> users;private final SqlSessionFactory sqlSessionFactory;public MybatisPlusInsert2Task(List<UserP> users, SqlSessionFactory sqlSessionFactory) {this.users = users;this.sqlSessionFactory = sqlSessionFactory;}@Overridepublic void run() {if(!CollectionUtils.isEmpty(users)) {long start = System.currentTimeMillis();//批量插入userstry (SqlSession sqlSession = this.sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {UserMapper userMapper = sqlSession.getMapper(UserMapper.class);userMapper.batchAdd(users);sqlSession.commit();} catch (Exception e) {log.error("MybatisPlusInsertTask2 批量插入数据异常!e={}", e.getMessage());throw new RuntimeException(e);} finally {int total = users.size();long time = System.currentTimeMillis() - start;long totalTime = DataSourceUtil.atomicLong.addAndGet(time);log.info("MybatisPlusInsertTask2 批量插入{}条数据,耗时{}ms,totalTime={}", total, time, totalTime);}}}
}

注:测试有限,仅供参考。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

案例 百万级数据批量插入测试

1、jpa、mybatis-plus、jdbc、load data infile测试比较2、load data infile语法测试注:测试有限,仅供参考。雨淋淋过的季节

【向量嵌入】 大型语言模型的基础

一、向量是机器的语言 向量在 LLM 和生成式 AI 的功能中起着至关重要的作用。要了解它们的重要性,就需要理解什么是向量以及它们如何在 LLM 中生成和利用。 在数学和物理学中,向量是同时具有大小和方向的对象。它可以在几何上表示为有向线段,其中线的长度表示大小,箭头指向…

DeepSeekSelfTool :流量分析、JS代码审计、进程分析

# DeepSeek;# AI ;# 代码审计;# 0day;# 渗透测试;# 通用 免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,文章作者和本公众号不承担任何法律及连带责任,望周知!!! 前言 首个由DeepSe…

【安全运营】告警如何区分自己人

前言 安全运营小伙伴可能都遇到过这样的情况,安全系统突然出现大量告警,看告警详情信息真真切切,凭感觉就是有攻击者在搞事情,于是立马启动应急响应,进入紧急状态,查询源目IP信息,电话联系相关人员开启三连问:这是什么系统,目前有做什么操作,能不能临时停用。最后一顿…

panellook筛选所有18寸面板

筛选后方便查看当前有哪些型号 https://www.panelook.com/sizmodlist.php?sizes[]=1800

大米CMS靶场支付漏洞

在大米CMS上购买一个商品,选择站内扣款并提交订单,BP抓包2. 根据抓包内容找到带有敏感信息的数据包(URL 带有dobuy),分析敏感信息 根据数据包中的信息我们可以获取下面可能有用的敏感信息 商品id,数量qty,价格price, 其他信息:是否购物车结算iscart,付款方式trade_type…

Spacedesk: 轻松实现手机控制电脑

这款强大的软件可以将您手头的手机或平板变身为电脑的第二块显示屏,轻松实现屏幕面积的扩展。告别昂贵的外接显示器,Spacedesk 为您打造无限可能的工作空间。最大的亮点在于,Spacedesk 不仅能扩展屏幕,还支持触控操作。您可以在手机或平板上直接进行各种操作,体验全新的交互方式…

DeepSeek开启AI办公新模式,WPS/Office集成DeepSeek-R1本地大模型!

从央视到地方媒体,已有多家媒体机构推出AI主播,最近杭州文化广播电视集团的《杭州新闻联播》节目,使用AI主持人进行新闻播报,且做到了0失误率,可见AI正在逐渐取代部分行业和一些重复性的工作,这一现象引发很多人的关注;前两天分享了一篇关于如何在Office和WPS中使用Deep…

vue+.net 利用signalR实现实时推送和显示通知消息

后端(.net) 创建SignalR Hub 创建类SysNoticeHub.cspublic class SysNoticeHub : Hub{}模拟接口发送在线通知 [Route("api/sysnotice")] [ApiController] public class SysNoticeController : BaseController {private readonly IHubContext<SysNoticeHub> _…

【SpringBoot代码审计】拿到了 SpringBoot 开发的系统,该如何代码审计分析漏洞?

众所周知,Java 语言生态在中大型企业中的使用率居高不下,可谓是如日中天,经久不衰。 使用 SpringBoot 架构来开发的 JavaWeb 系统,更是数不胜数,也因此我们渗透测试中一部分的挑战,可以说是来对抗 Java 代码。 如果我们拿到了基于 SpringBoot 开发的系统,想要进行代码审…

分组密码工作模式-ECB

一般分组密码加密分组大小为一固定长度,如128比特。如果消息长度超过固定分组长度时,在进行加密前,消息将被按照分组长度进行分块;如果消息长度不是分组长度的整数倍,则在分块后必须将其填充为分组长度的整数倍。 ECB模式是一种最直接的消息加密方法 ECB模式的加密和解密流…