一.需求分析

  最近接到一个需求,导入十万级,甚至可能百万数据量的记录了车辆黑名单的Excel文件,借此机会分析下编码过程;

  首先将这个需求拆解,发现有三个比较复杂的问题:

  问题一:Excel文件导入后首先要被解析为存放对象的列表,数据量大的情况下可能会导致内存溢出,解析时间过长;

  问题二:插入数据库的时候,数据量大,写入的时间长

  问题三:要对数据库中的现有数据进项判断,不仅仅要做插入动作,还要将数据库的数据与导入的数据对比,判断是否做更新操作

  

  其中:

  问题一和问题三,可以看做同一类,因为主要涉及内存计算导致的性能问题,以及内存占用过大的溢出问题,

  关于这两个问题,现在线上的机器基本上是4核8G的配置集群部署,内存并不是关键,我会在另一篇文章中给出我的方案,

  今天主要针对问题二,写入的数据库的问题给出我的方案,

  

  问题二主要是多次写入数据库的问题,显然,如果有几十万条数据,那么是不可能连续写几十万次的,不然要写到后年马月才能全部入库,

  解决方案:

    这里我主要采用了多线程的写入方式,十万条数据,2000条写一次(可以自己定义),用线程池提交多个线程任务同时写入,提高性能

二.代码环境

  Springboot2.1.3+POI+PGSQL

  controller层代码

    @PostMapping("/upload")
public void upload1(MultipartFile file, @Validated UploadReq req) throws Exception {
//从数据库查询出现有的数据,根据去重的字段分组去构建成一个HashMap,通过containsKey()判断
//将需要更新的数据放到updateList中
List<User> updateList=new ArrayList<>(); //已取值的行数
int rowNum = 0;
//列号
int colNum = 0;
//真正有数据的行数
int realRowCount = 0;
//得到工作空间
Workbook workbook = null; try {
workbook = ExcelUtil.getWorkbookByInputStream(file.getInputStream(), file.getOriginalFilename());
} catch (IOException e) {
e.printStackTrace();
}
//得到工作表
int numberOfSheets = workbook.getNumberOfSheets();
for (int i = 0; i < numberOfSheets; i++) {
Sheet sheet = ExcelUtil.getSheetByWorkbook(workbook, i)
realRowCount = sheet.getPhysicalNumberOfRows();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<User> list = new ArrayList<>();
User user = null; for(Row row:sheet) {
if(realRowCount == rowNum) {
break;
}
//空行跳过
if(ExcelUtil.isBlankRow(row)) {
continue;
}
if(row.getRowNum() == -1) {
continue;
}else {
//第一行表头跳过
if(row.getRowNum() == 0) {
continue;
}
}
rowNum ++;
colNum = 1;
user = new User();
ExcelUtil.validCellValue(sheet, row, colNum, "id");
user.setId(Integer.valueOf(ExcelUtil.getCellValue(sheet, row, colNum - 1)));
ExcelUtil.validCellValue(sheet, row, ++ colNum, "name");
user.setId(Integer.valueOf(ExcelUtil.getCellValue(sheet, row, colNum - 1)));
//判断是否是已存在的数据,如果是就更新,不是就新增
//updateList.add(user);
list.add(user); } //新增的逻辑
userService.saveBatch(list);
System.out.println(list);
}
}

  service层代码

@Service
public class UserServiceImpl implements IUserService { @Autowired
private UserMapper userMapper; @Override
public void saveBatch(List<User> list) throws Exception {
//一个线程处理200条数据
int count = 200;
//数据集合大小
int listSize = list.size();
//开启的线程数
int runSize = (listSize / count) + 1;
//存放每个线程的执行数据
List<User> newlist = null; //创建一个线程池,数量和开启线程的数量一样
//Executors 的写法
// ExecutorService executor = Executors.newFixedThreadPool(runSize); //ThreadPoolExecutor的写法
ThreadPoolExecutor executor = new ThreadPoolExecutor(runSize, runSize, 1,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.DiscardOldestPolicy()); //创建两个个计数器
CountDownLatch begin = new CountDownLatch(1);
CountDownLatch end = new CountDownLatch(runSize);
//循环创建线程
for (int i = 0; i < runSize; i++) {
//计算每个线程执行的数据
if ((i + 1) == runSize) {
int startIndex = (i * count);
int endIndex = list.size();
newlist = list.subList(startIndex, endIndex);
} else {
int startIndex = (i * count);
int endIndex = (i + 1) * count;
newlist = list.subList(startIndex, endIndex);
}
//线程类
ImportThread mythead = new ImportThread(newlist, begin, end,userMapper);
//这里执行线程的方式是调用线程池里的executor.execute(mythead)方法。
executor.execute(mythead);
}
begin.countDown();
end.await();
//执行完关闭线程池
executor.shutdown();
}

  线程类

public class ImportThread implements Runnable {

    public ImportThread() {
} UserMapper userMapper;
private List<User> list;
private CountDownLatch begin;
private CountDownLatch end; /**
* 方法名: ImportThread
* 方法描述: 创建个构造函数初始化 list,和其他用到的参数
* @throws
*/
public ImportThread(List<User> list, CountDownLatch begin, CountDownLatch end,UserMapper userMapper) {
this.list = list;
this.begin = begin;
this.end = end;
this.userMapper=userMapper;
} @Override
public void run() {
try {
//执行完让线程直接进入等待
userMapper.saveBatch(list);
begin.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//这里要主要了,当一个线程执行完 了计数要减一不然这个线程会被一直挂起
//这个方法就是直接把计数器减一的
end.countDown();
}
} }

  

  

  

十万级百万级数据量的Excel文件导入并写入数据库的更多相关文章

  1. NodeJs之EXCEL文件导入导出MongoDB数据库数据

    NodeJs之EXCEL文件导入导出MongoDB数据库数据 一,介绍与需求 1.1,介绍 (1),node-xlsx : 基于Node.js解析excel文件数据及生成excel文件. (2),ex ...

  2. 读取FTP上的excel文件,并写入数据库

    今天遇到一些问题,需要从ftp上读取一些excel文件,并需要将excel中的数据写入到数据库,这样就可以通过管理页面查看这些数据. 我将相关工作分为三步,1.从ftp上读取相关文件,并将excel文 ...

  3. 批量将制定文件夹下的全部Excel文件导入微软SQL数据库

    以下代码将c:\cs\文件夹下的全部Excle中数据导入到SQL数据库 declare @query vARCHAR(1000) declare @max1 int declare @count1 i ...

  4. PHP Excel文件导入数据到数据库

    1.php部分(本例thinkphp5.1): 下载PHPExcel了扩展http://phpexcel.codeplex.com/ <?phpnamespace app\admin\contr ...

  5. Excel文件导入SQL Server数据库

    Excel表格的使用可谓是非常广泛,博主也简单百度了一下Excel的发展. 发展历程: 1982年 Microsoft推出了它的第一款电子制表软件-Multiplan,并在CP/M系统上大 Excel ...

  6. Excel文件导入SQL Server数据库表

    --office 2003--如果接受数据导入的表已经存在insert into DemoTable select * from OPENROWSET('MICROSOFT.JET.OLEDB.4.0 ...

  7. PHP实时生成并下载超大数据量的EXCEL文件

    最近接到一个需求,通过选择的时间段导出对应的用户访问日志到excel中, 由于用户量较大,经常会有导出50万加数据的情况.而常用的PHPexcel包需要把所有数据拿到后才能生成excel, 在面对生成 ...

  8. Excel导入导出工具(简单、好用且轻量级的海量Excel文件导入导出解决方案.)

    Excel导入导出工具(简单.好用且轻量级的海量Excel文件导入导出解决方案.) 置顶 2019-09-07 16:47:10 $9420 阅读数 261更多 分类专栏: java   版权声明:本 ...

  9. (转)如何将 Excel 文件导入到 Navicat for MySQL 数据库

    场景:工作中需要统计一段时间的加班时长,人工统计太过麻烦,就想到使用程序实现来统计 1 如何将 Excel 文件导入到 Navicat for MySQL 数据库 Navicat for MySQL  ...

随机推荐

  1. PeStudio读取pe信息

    https://blog.csdn.net/x_xx_xxx_xxxx/article/details/79867928 PeStudio  主要利用此界面工具 https://blog.csdn.n ...

  2. Swoole 简单学习(2)

    Swoole 简单学习(2) swoole之tcp服务器: //创建tcp服务器new swoole_server(string $host,int $port,int $mode=SWOOLE_PR ...

  3. 【Leetcode周赛】从contest-81开始。(一般是10个contest写一篇文章)

    Contest 81 (2018年11月8日,周四,凌晨) 链接:https://leetcode.com/contest/weekly-contest-81 比赛情况记录:结果:3/4, ranki ...

  4. javascript实现下拉菜单的显示与隐藏

    demo.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  5. Java中File类重修

    IO流 概述 io流:输入输出流(input/output).流是一组有顺序的,有起点和终点的字节集合,是对各种数据传输的总称或抽象.即数据在两设备之间的传输称为流.流的本质是数据传输. InputS ...

  6. Mac下通过npm安装webpack 、vuejs,以及引入jquery、bootstrap等(初稿)

    前言: 初次接触前端开发技术,一些方向都是在同事的指引和自己的探索后,跑了个简易web,迈入全栈系列.由于是事后来的文章,故而暂只是杂记,写的粗略且不清晰,后续将补之. 主要参考文档: http:// ...

  7. Codeforces 803F - Coprime Subsequences(数论)

    原题链接:http://codeforces.com/contest/803/problem/F 题意:若gcd(a1, a2, a3,...,an)=1则认为这n个数是互质的.求集合a中,元素互质的 ...

  8. 2019牛客多校第六场 B - Shorten IPv6 Address 模拟

    B - Shorten IPv6 Address 题意 给你\(128\)位的二进制,转换为十六进制. 每\(4\)位十六进制分为\(1\)组,每两组用一个\(":"\)分开. 每 ...

  9. docker-swarm笔记

    1.部署环境: centos7 创建三节点的 swarm 集群 swarm-manager 是 manager node : 192.168.1.150 swarm-worker1 和 swarm-w ...

  10. 防止NSTimer和调用对象之间的循环引用

    防止NSTimer和调用对象之间的循环引用 @interface NSTimer (EOCBlocksSupport) + (NSTimer *)eoc_scheduledTimerWithTimeI ...