十万级百万级数据量的Excel文件导入并写入数据库
一.需求分析
最近接到一个需求,导入十万级,甚至可能百万数据量的记录了车辆黑名单的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文件导入并写入数据库的更多相关文章
- NodeJs之EXCEL文件导入导出MongoDB数据库数据
NodeJs之EXCEL文件导入导出MongoDB数据库数据 一,介绍与需求 1.1,介绍 (1),node-xlsx : 基于Node.js解析excel文件数据及生成excel文件. (2),ex ...
- 读取FTP上的excel文件,并写入数据库
今天遇到一些问题,需要从ftp上读取一些excel文件,并需要将excel中的数据写入到数据库,这样就可以通过管理页面查看这些数据. 我将相关工作分为三步,1.从ftp上读取相关文件,并将excel文 ...
- 批量将制定文件夹下的全部Excel文件导入微软SQL数据库
以下代码将c:\cs\文件夹下的全部Excle中数据导入到SQL数据库 declare @query vARCHAR(1000) declare @max1 int declare @count1 i ...
- PHP Excel文件导入数据到数据库
1.php部分(本例thinkphp5.1): 下载PHPExcel了扩展http://phpexcel.codeplex.com/ <?phpnamespace app\admin\contr ...
- Excel文件导入SQL Server数据库
Excel表格的使用可谓是非常广泛,博主也简单百度了一下Excel的发展. 发展历程: 1982年 Microsoft推出了它的第一款电子制表软件-Multiplan,并在CP/M系统上大 Excel ...
- Excel文件导入SQL Server数据库表
--office 2003--如果接受数据导入的表已经存在insert into DemoTable select * from OPENROWSET('MICROSOFT.JET.OLEDB.4.0 ...
- PHP实时生成并下载超大数据量的EXCEL文件
最近接到一个需求,通过选择的时间段导出对应的用户访问日志到excel中, 由于用户量较大,经常会有导出50万加数据的情况.而常用的PHPexcel包需要把所有数据拿到后才能生成excel, 在面对生成 ...
- Excel导入导出工具(简单、好用且轻量级的海量Excel文件导入导出解决方案.)
Excel导入导出工具(简单.好用且轻量级的海量Excel文件导入导出解决方案.) 置顶 2019-09-07 16:47:10 $9420 阅读数 261更多 分类专栏: java 版权声明:本 ...
- (转)如何将 Excel 文件导入到 Navicat for MySQL 数据库
场景:工作中需要统计一段时间的加班时长,人工统计太过麻烦,就想到使用程序实现来统计 1 如何将 Excel 文件导入到 Navicat for MySQL 数据库 Navicat for MySQL ...
随机推荐
- R语言中的数据分析函数
数学类函数 在求有缺失值的子集的最大值时候,需要先用na.re=TRUE去掉缺失值. 求几个特定百分位数 round(x,n) n表示保留的小数点位数 分布类函数 rnorm,密度高的数字,生成概率就 ...
- MySQL--19 MHA切换日志分析
MHA切换检测日志分析 GTID模式 [root@db03 ~]# tail -f /etc/mha/manager.log #在MySQL select ping:2006上出错(MySQL服务器已 ...
- procixx地址
\\192.168.35.7\Download\Builds\procixx_psoc
- 第03章 AOP前奏
第03章 AOP前奏 提出问题 ●情景:数学计算器 ●要求 ①执行加减乘除运算 ②日志:在程序执行期间追踪正在发生的活动 ③验证:希望计算器只能处理正数的运算 ●常规实现 ●问题 ○代码混乱:越来越多 ...
- torchvision.transforms模块介绍
torchvision.transforms模块 官网地址:https://pytorch.org/docs/stable/torchvision/transforms.html# torchvisi ...
- [AtCoder] NIKKEI Programming Contest 2019 (暂缺F)
[AtCoder] NIKKEI Programming Contest 2019 本来看见这一场的排名的画风比较正常就来补一下题,但是完全没有发现后两题的AC人数远少于我补的上一份AtCoder ...
- shell编程基础知识3
1.Linux下scp的用法 scp就是secure copy,一个在linux下用来进行远程拷贝文件的命令.有时我们需要获得远程服务器上的某个文件,该服务器既没有配置ftp服务器,也没有做共享,无法 ...
- MySQL不支持的特性
MySQL 1.不支持物化视图. 2.不支持位图索引. 3.不支持并行查询. 4.不支持哈希关联,MySQL的所有关联都是嵌套循环关联.不过,可以通过建立一个哈希索引来曲线实现. 5.不允许对同一表同 ...
- springboot+mybatis搭建web项目
使用idea+springboot+Mybatis搭建一个简单的web项目. 首先新建一个项目: 在这里选择Maven项目也可以,但是IDEA为我们提供了一种更方便快捷的创建方法,即Spring In ...
- 【leetcode】564. Find the Closest Palindrome
题目如下: 解题思路:既然是要求回文字符串,那么最终的输出结果就是对称的.要变成对称字符串,只要把处于对称位置上对应的两个字符中较大的那个变成较小的那个即可,假设n=1234,1和4对称所以把4变成1 ...