技术摘要:springboot线程池、mybatis

〇、场景

目标:短时间内实现百万级数据落库

txt文件格式,每行为一条数据,共200w条,如下:

|identity|name|age|address|desc|

|123123|张三|20|xxxxxxxxxxx|xxxxxx|

|321321|李四|22|xxxxxxxx||

|112233|王五|21|xxxxxxxxxxxx|xxxxxxxx|

......

一、流程

1. 读取文件

目标:将txt文件读取到系统中

通过IO流的方式读取文件,设置路径和编码格式

数据列数(column):字段数,分隔符号(delimiter),字符截取的时候会使用

//数据列数
int column = 5;
//分割符号
String delimiter = "|";
//文件绝对路径
String path = "C:\\Users\\Admin\\Desktop\\test.txt";
//编码格式
String encoding = "UTF-8";//IO流读取文件
File file = new File(path);
InputStreamReader in = new InputStreamReader(new FileInputStream(file),encoding);
BufferedReader read = new BufferedReader(in);

2. 字符截取将数据存入集合

目标:将数据流存储到到实体列表中

每次while循环,截取文本中一行数据“|identity|name|age|address|desc|”

①得到分隔符(delimiter)的索引数组:将单条数据分割成单个字符存到数组(charArr),遍历charArr将delimiter的索引存到数组(indexArr)

②根据indexArr就可以截取分隔符之间的数据,得到数据数组(strArr)

③遍历strArr将数据存在实体(person)中,然后再add到集合(personList)中

注:为什么不直接用lineTxt.split("|"),因为数据存在空值“||”分隔符间没有数据会导致不正确的截取

String lineTxt = null;
List<Person> personList = new ArrayList<>();
while((lineTxt = read.readLine()) != null){//分割为字符数组String[] charArr = lineTxt.split("");//存储“|”在数组中的的indexint[] indexArr = new int[column+1];int j = 0;for (int i = 0; i < charArr.length; i++) {if(delimiter.equals(charArr[i])){indexArr[j] = i;j++;}}//存储“|”与“|”之间的数据,如果没有则为nullString[] strArr = new String[column];int k = 0;for (int i = 0; i < indexArr.length; i++) {if(i < indexArr.length-1){//截取两个相邻index之间的字符并去掉两端的空格,如果为”||“strArr[k] = lineTxt.substring(indexArr[i]+1,indexArr[i+1]).trim();k++;}}//存入实体类Person person = new Person();for (int i = 0; i < strArr.length; i++) {person.setId(UUID.randomUUID().toString());person.setIdentity(strArr[0]);person.setName(strArr[1]);person.setAge(strArr[2]);person.setAddress(strArr[3]);person.setDesc(strArr[4]);}//添加到集合personList.add(person);
}

3. 数组截取并落库

目标:实现分批落库

得到长度为200w的集合后,不能直接批量插入,会导致IO异常,单次批量插入上限2100条左右;需要对数组逐步截取,这里选择的是1000条为一批,每1000条数据落库一次

//单次分批落库条数
int subList = 1000;
//计算执行次数
int count = personList.size() % subList > 0 ? (personList.size()/subList)+1 : personList.size()/subList;
//临时集合
List<Person> tempList;
for (int i = 0; i < count; i++) {//截取集合tempList = personList.stream().skip(subList*i).limit(subList).collect(Collectors.toList());//数据落库personDao.insertBatch(personList);
}

mybatis中,使用批量插入的方式落库,可以提高插入的效率

<insert id="insertBatch" parameterType="com.example.pojo.Person">insert into person(id,identity,name,age,address,desc) values<foreach collection="personList" item="item" index="index" separator="," >(#{item.id,jdbcType=VARCHAR},#{item.identity,jdbcType=VARCHAR},#{item.name,jdbcType=VARCHAR},#{item.age,jdbcType=INTEGER},#{item.address,jdbcType=VARCHAR},#{item.desc,jdbcType=VARCHAR})</foreach>
</insert>

4.多线程方式落库

目标:实现异步落库

使用springboot线程池,多线程可以提高落库效率,for循环走的很快,需要使用CountDownLatch中await方法,等待全部线程执行完毕再结束

//单次分批落库条数
int subList = 1000;
//计算执行次数
int count = personList.size() % subList > 0 ? (personList.size()/subList)+1 : personList.size()/subList;
//临时集合
List<Person> tempList;
CountDownLatch countDownLatch = new CountDownLatch(personList.size()/subList);
for (int i = 0; i < count; i++) {//截取集合tempList = personList.stream().skip(subList*i).limit(subList).collect(Collectors.toList());//多线程执行落库方法asyncService.executeAsync(tempList, countDownLatch);
}
//阻塞线程,等待全部线程执行完毕
countDownLatch.await();

springboot线程池代码

@Service
@EnableAsync
public class AsyncService {@Autowiredprivate PersonDao personDao;@Async("taskExecutor")public void executeAsync(List<Person> personList, CountDownLatch countDownLatch) {try{personDao.insertBatch(personList);} finally {countDownLatch.countDown();}}
}
@Configuration
@EnableAsync
public class ExecutorConfig {ThreadPoolProperties properties = new ThreadPoolProperties();@Bean(name = "taskExecutor")public ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(properties.getCorePoolSize());executor.setMaxPoolSize(properties.getMaxPoolSize());executor.setQueueCapacity(properties.getQueueCapacity());executor.setThreadNamePrefix(properties.getThreadNamePrefix());executor.setKeepAliveSeconds(properties.getKeepAliveTime());executor.setWaitForTasksToCompleteOnShutdown(properties.isWaitForTasksToCompleteOnShutdown());executor.setAwaitTerminationSeconds(properties.getAwaitTerminationSeconds());executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;}@Dataclass ThreadPoolProperties {private int corePoolSize = 30;private int maxPoolSize = 100;private int keepAliveTime;private int queueCapacity = 1000;private String threadNamePrefix = "test";private boolean allowCoreThreadTimeout = false;private boolean waitForTasksToCompleteOnShutdown = false;private int awaitTerminationSeconds;}
}

二、总结

通过打时间戳记录时间,读取文件到存储实体集合用时10秒,时间主要花费在落库;单线程总用时18分钟左右,多线程4分钟;还可以将表的所有字段取消主键约束和非空约束提高落库效率,最终优化后总用时仅32秒!


原创不易,转载请注明来源

SpringBoot线程池实现200w数据快速落库相关推荐

  1. Springboot线程池的使用和扩展

    我们常用ThreadPoolExecutor提供的线程池服务,springboot框架提供了@Async注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行,今天我们就来实战体验这个线程池服务: 实 ...

  2. SpringBoot 线程池,也太好用了叭!

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:blog.csdn.net/m0_37701381/article/details/81072774 前言 前两天做项目的时候 ...

  3. SpringBoot线程池的创建、@Async配置步骤及注意事项

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Muscleheng blog.csdn.net/Muscl ...

  4. SpringBoot线程池ThreadPoolTaskExecutor和@Async异步方法浅解及代码应用示例

    目录 线程池执行顺序 线程池配置策略 Spring线程池的配置类: Spring有.无返回值的异步调用示例 自定义的异步方法类代码: 测试类代码 常见问题: 参考文章: 线程池执行顺序 核心线程数(C ...

  5. JAVA使用线程池查询大批量数据

    前言 在开发过程中可能会碰到某些独特的业务,比如查询全部表数据,数据量过多会导致查询变得十分缓慢.虽然在大多数情况下并不需要查询所有的数据,而是通过分页或缓存的形式去减少或者避免这个问题,但是仍然存在 ...

  6. springboot 线程池_Spring boot 2 线程池怎么配置

    线程池 在java 中线程池,就是ThreadPoolExecutor来构造,简单看下线程池包含的方法, corePoolSize:初始化线程.线程池中保留的线程数量. maximumPoolSize ...

  7. springboot线程池配置

    1. application.yml配置 # 异步线程配置 # 配置核心线程数 async.executor.thread.core_pool_size: 10 # 配置最大线程数 async.exe ...

  8. flink实时流遇到的问题排查——部分数据未落库redis问题

    flink实时流遇到的问题排查 1.技术和环境 2.问题表述 3.简化的代码 4.问题排查思路 5.结论 6.后续补充 1.技术和环境 技术:kafka.zookeeper.DataStream.re ...

  9. springboot线程池使用

    1.配置@Configuration @EnableAsync public class TaskPoolConfig {@Bean("taskExecutor")public E ...

  10. Springboot应用中线程池配置教程(2021版)

    前言:日常开发中我们常用ThreadPoolExecutor提供的线程池服务帮我们管理线程,在Springboot中更是提供了@Async注解来简化业务逻辑提交到线程池中执行的过程.由于Springb ...

最新文章

  1. python科学计算整理
  2. dz 数据表分析!!!
  3. cuda9.1 tensorflow1.6
  4. 函数传参字典_Python 函数参数解包
  5. 论述类文本知识框架_考前知识梳理与答题技巧之论述类文本
  6. iframe高度自适应,终于解决了
  7. 前后端分离Java后端跨越问题解决
  8. OpenStack基础知识
  9. nginx 安装_安装Nginx的几种方式
  10. 默认文件夹为空,如何从Microsoft Edge下载收藏夹
  11. C++ 实数和二进制操作入门
  12. android小项目数字拼图游戏_Java小项目之:拼图游戏
  13. 搭建Struts框架
  14. CentOS7版本下载地址发布 附正确下载CentOS各个版本镜像
  15. 贾俊平统计学思维导图- 第八章 假设检验
  16. 预防胜于补救 《热血江湖》防盗号傻瓜手册(转)
  17. uni-app 获取屏幕亮度与设置屏幕亮度
  18. 工业控制计算机固态硬盘,工业级固态硬盘的标准有哪几类
  19. 记录一次iPhone5s的iCloud bypass经历
  20. 腾讯云短信服务(SMS)申请流程

热门文章

  1. linux系统漏洞firefart,记:从文件上传漏洞到获得root权限
  2. AWS-EC2域名映射
  3. C# 实现eval,支持任意个数、任意基本类型的参数
  4. Android 、AndroidQ 、小米、 oppo等【后台启动界面】问题 解决方案
  5. mysql 错误代码1130_mysql出现错误码1130怎么办
  6. AtCoder Beginner Contest 156 D Bouquet 失之交臂 容斥原理+二项式定理+乘法逆元+快速幂+模后负数 多余担心
  7. 赶上了秋招的末班车,抓住了秋招的尾巴,成功上岸了
  8. 实战分布式之电商高并发秒杀
  9. java fillrect_Java graphics2D fillRect无法使用半透明颜色正常...
  10. ​云队友丨两次疫情冲击,却两次成功上市,携程是怎么做到的?