您当前的位置:首页 > 互联网百科 > 大数据

解析csv文件,读取百万级数据

时间:2022-11-30 16:14:16  来源:今日头条  作者:马士兵教育CTO


 

最近在处理下载支付宝账单的需求,支付宝都有代码示例,功能完成还是比较简单的,唯一的问题就在于下载后的文件数据读取。账单文件可大可小,要保证其可用以及性能就不能简单粗暴地完成开发就行。

文件下载是是csv格式,此文件按照行读取,每一行中各列数据直接用逗号,隔开的。

前置设置:

 

  1. 开启了设置内存大小以及GC日志输出配置-Xms800m -Xmx800m -XX:+PrintGCDetAIls
  2. 测试文件total-File.csv数据量: 100万,文件大小:176M
  3. 定义账单文件的属性字段:
private static final List ALI_FINANCE_LIST = new ArrayList<>( Arrays.asList("FINANCE_FLOW_NUMBER", "BUSINESS_FLOW_NUMBER", "MERCHANT_ORDER_NUMBER", "ITEM_NAME", "CREATION_TIME", "OPPOSITE_ACCOUNT", "RECEIPT_AMOUNT", "PAYMENT_AMOUNT", "ACCOUNT_BALANCE", "BUSINESS_CHANNEL", "BUSINESS_TYPE", "REMARK")); 复制代码

 

相关推荐阅读:

图形化监控工具Jconsole

虚拟机的日志和日志参数

第一版:简单粗暴

直来直往,毫无技巧

 

拿到文件流,直接按行读取,把所有的数据放入到List中(其中业务相关的校验以及数据筛选都去掉了)

代码如下

@ApiOperation(value = "测试解析-简单粗暴版") @GetMApping("/readFileV1") public ResponseEntity readFileV1(){ File file = new File("/Users/ajisun/projects/alwaysCoding/files/total-file.csv"); List> context = new ArrayList<>(); try ( InputStream stream = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(stream, StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr) ) { String line = ""; int number = 1; while ((line = br.readLine()) != null) { //去除#号开始的行 if (!line.startsWith("#")) { if (number >= 1) { //csv是以逗号为区分的文件,以逗号区分 String[] columns = line.split(",", -1); //构建数据 Map dataMap = new HashMap<>(16); for (int i = 0; i < columns.length; i++) { //防止异常,大于预定义的列不处理 if (i > ALI_FINANCE_LIST.size()) { break; } dataMap.put(ALI_FINANCE_LIST.get(i), columns[i].trim()); } context.add(dataMap); } number++; } } // TODO 存表 System.out.println("=====插入数据库,数据条数:"+context.size()); System.out.println("对象大小:"+(ObjectSizeCalculator.getObjectSize(context)/1048576) +" M"); context.clear(); } catch (Exception e) { e.printStackTrace(); } return null; } 复制代码

输出日志以及Jconsole的监控如下


 


 

 

由上面的图可以看出内存和CPU的使用率都比较高,会不断触发Full GC,最终还出现了OOM,内存基本使用完了,cpu使用也达到了近70%。

 

去除-Xms800m -Xmx800m的内存大小限制后可以把全部数据拿到,结果如下图所示


 


 

 

所有数据可以正常解析读取,Full GC也没用前一次频繁,没有出现OOM。10w条数据大小有1.2G,所占用的内存更是达到2.5G,CPU也是近60%的使用率。

 

仅仅是200M的csv文件,堆内存就占用了2.5G,如果是更大的文件,内存占用不得起飞了

严重占用了系统资源,对于大文件,此方法不可取。

第二版:循序渐进

缓缓图之,数据分批

 

第一版内存、CPU占用过大,甚至OOM,主要原因就是把所有数据全部加载到内存了。为了避免这种情况,我们可以分批处理。

参数说明:

 

  • file:解析的文件
  • batchNumOrder:批次号
  • context:存放数据的集合
  • count:每一批次的数据量
1. 接口API@ApiOperation(value = "测试解析-数据分批版") @GetMapping("/readFileV2") public ResponseEntity readFileV2(@RequestParam(required = false) int count) { File file = new File("/Users/ajisun/projects/alwaysCoding/files/total-file.csv"); List> context = new ArrayList<>(); int batchNumOrder = 1; parseFile(file, batchNumOrder, context, count); return null; } 复制代码2. 文件解析
文件解析,获取文件流
private int parseFile(File file, int batchNumOrder, List> context, int count) { try ( InputStreamReader isr = new InputStreamReader(new FileInputStream(file) , StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr) ) { batchNumOrder = this.readDataFromFile(br, context, batchNumOrder, count); } catch (Exception e) { e.printStackTrace(); } return batchNumOrder; } 复制代码3. 读取文件数据
按行读取文件,分割每行数据,然后按照#{count}的数量拆分,分批次存储
private int readDataFromFile(BufferedReader br, List> context, int batchNumOrder, int count) throws IOException { String line = ""; int number = 1; while ((line = br.readLine()) != null) { //去除#号开始的行 if (!line.startsWith("#")) { if (number >= 1) { //csv是以逗号为区分的文件,以逗号区分 String[] columns = line.split(",", -1); //构建数据 context.add(constructDataMap(columns)); } number++; } if (context.size() >= count) { // TODO 存表 System.out.println("=====插入数据库:批次:" + batchNumOrder + ",数据条数:" + context.size()); context.clear(); batchNumOrder++; } } // 最后一批次提交 if (CollectionUtils.isNotEmpty(context)) { System.out.println("=====插入数据库:批次:" + batchNumOrder + ",数据条数:" + context.size()); context.clear(); } return batchNumOrder; } 复制代码4.组装数据
把每一行数据按照顺序和业务对象ALI_FINANCE_LIST匹配 ,组装成功单个map数据
public Map constructDataMap(String[] columns) { Map dataMap = new HashMap<>(16); for (int i = 0; i < columns.length; i++) { //防止异常,大于预定义的列不处理 if (i > ALI_FINANCE_LIST.size()) { break; } dataMap.put(ALI_FINANCE_LIST.get(i), columns[i].trim()); } return dataMap; } 复制代码5.执行结果

 


 


 

把文件分批读取插入数据库,可以减少内存的占用以及解决高CPU的问题。已经可以很好地处理文件读取问题了。

但是如果一个文件更大,有1G,2G 甚至更大,虽然不会造成OOM ,但是整个解析的时间就会比较长,然后如果中间出现问题,那么就需要从头再来。

假如是1000万数据的文件,按照一批次1万条插入数据库,然而到999批次的时候失败了(不考虑回滚),那么为了保证数据的完整性,该文件就需要重新上传解析。但实际上只需要最后一批次数据即可, 多了很多重复操作。

可以使用另一种方式处理,第三版

第三版:大而化小

分而治之,文件拆分

 

主要改动就是在第二版的基础增加文件拆分的功能,把一个大文件按照需求拆分成n个小文件,然后单独解析拆分后的小文件即可。其他方法不变。

1.接口API

获取拆分后的文件,循环解析读取
@ApiOperation(value = "测试解析-文件拆分版") @GetMapping("/readFileV3") public ResponseEntity readFileV3(@RequestParam(required = false) int count){ if (StringUtils.isEmpty(date)) { this.execCmd(); } File file = new File("/Users/ajisun/projects/alwaysCoding/files"); File[] childs = file.listFiles();//可以按照需求自行排序 for (File file1 : childs) { if (!file1.getName().contains(".csv") && file1.getName().contains("total-file-")) { file1.renameTo(new File(file1.getAbsolutePath() + ".csv")); } } int batchNumOrder = 1; List> context = new ArrayList<>(); for (File child : childs) { if (!child.getName().contains("total-file-")){ continue; } batchNumOrder = parseFile(child, batchNumOrder, context, count); } return null; } 复制代码2.文件拆分
按照需求使用linux命令拆分文件,大而化小,然后按照一定规则命名
public List execCmd() { List msgList = new ArrayList(); String command = "cd /Users/ajisun/projects/alwaysCoding/files && split -a 2 -l 10000 total-file.csv total-file-"; try { ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", command); Process process = pb.start(); BufferedReader ir = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = ir.readLine()) != null) { msgList.add(line); } } catch (IOException e) { e.printStackTrace(); } System.out.println(msgList); return msgList; } 复制代码

 

这种方式的处理在内存与CPU的占用和第二版基本没有差别。

如果采用这种方式记得文件的清理,避免磁盘空间的占用

技术扩展:文件拆分cd /Users/ajisun/projects/alwaysCoding/files && split -a 2 -l 10000 total-file.csv total-file- 复制代码

上述字符串是两个命令用&&连接,第一个是进入到指定文件夹,第二个就是按照10000行拆分total-file.csv,而且子文件命名以total-file-开头,后缀默认两位字母结尾. 执行后的结果如下图


 

 

mac下不能用数字命名(Linux下可以的),只能是默认的字母命名

 

Linux下:ajisun.log文件按照文件大小50m切割,后缀是2位数字结尾的子文件,子文件以ajisun-开头


 

总结总结

如果确定了解析的文件都是小文件,而且文件中的数据最多也就几万行,那么直接简单粗暴使用第一版也没问题。

如果文件较大,几十兆,或者文件中的数据有大几十万行,那么就使用第二版的分批处理。

如果文件很大,以G为单位,或者文件中的数据有几百万行,那么就使用第三版的文件拆分

这里只是做文件解析以及读取相关的功能,但是在实际情况中可能会存在各种各样的数据校验,这个需要根据自己的实际情况处理,但是要避免在解析大文件的时候循环校验,以及循环操作数据库。必要时还可以引入中间表存储文件数据(不做任何处理),在中间表中做数据校验 再同步到目标表。



Tags:csv文件   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
使用Python进行文本分析-将PDF文件多进程批量处理为csv文件
在文本分析的过程中,将原始数据转换为TXT文件非常关键,主要出于以下几个方面的考虑:1.格式简单与统一: TXT文件是一种简单的文本格式,只包含纯文本信息,不包含任何格式或样式信息...【详细内容】
2023-11-14  Search: csv文件  点击:(295)  评论:(0)  加入收藏
解析csv文件,读取百万级数据
最近在处理下载支付宝账单的需求,支付宝都有代码示例,功能完成还是比较简单的,唯一的问题就在于下载后的文件数据读取。账单文件可大可小,要保证其可用以及性能就不能简单粗暴...【详细内容】
2022-11-30  Search: csv文件  点击:(209)  评论:(0)  加入收藏
使用SpringBatch读取csv文件
1、需求系统每日从某个固定的目录中读取csv文件,并在控制台上打印。2、解决方案要解决上述需求,可以使用的方法有很多,此处选择使用Spring Batch来实现。3、注意事项1、文件路...【详细内容】
2022-08-31  Search: csv文件  点击:(484)  评论:(0)  加入收藏
盘点CSV文件在Excel中打开后乱码问题的两种处理方法
大家好,我是Python进阶者。前几天给大家分享了一些乱码问题的文章,阅读量还不错,感兴趣的小伙伴可以前往:盘点3种Python网络爬虫过程中的中文乱码的处理方法,UnicodeEncodeError:...【详细内容】
2021-11-01  Search: csv文件  点击:(276)  评论:(0)  加入收藏
把JSON/CSV文件打造成MySQL数据库
生活中,你我一定都看到过这种「xx元爆改出租屋」,「爆改小汽车」之类的文章,做为IT人,折腾的劲头一点也不差。软件开发过程中,你是否有时候,会拿着业务提供的一个个CSV或者JSON的...【详细内容】
2021-01-06  Search: csv文件  点击:(323)  评论:(0)  加入收藏
golang操作csv文件
日常中经常需要使用 golang 读写 csv 文件,比如将数据库中的数据按照要求导出到 csv 中,读取 csv 文件中的数据到内存中进行处理等操作。ps: 如果是 Excel 文件(*.xlsm、.xls...【详细内容】
2019-11-13  Search: csv文件  点击:(888)  评论:(0)  加入收藏
向oracle中导入*.csv文件
1.什么是*.csv,如何得到?里面存放的是数据表.每行代表数据库表格的一行,每行中,每两个数据中间由逗号","分割.*.csv可以通过"将excel文件另存为*.csv"得到.2.如何将*.csv格...【详细内容】
2019-07-11  Search: csv文件  点击:(1480)  评论:(0)  加入收藏
▌简易百科推荐
大数据杀熟何时告别“人人喊打却无可奈何”?
2月7日郑州飞往珠海的航班,不同手机、不同账号搜索该航班显示出不同价格。图源网络有网友近日分享在某平台的购票经历,引发社会广泛关注&mdash;&mdash;用3个账号买同一航班同...【详细内容】
2024-01-30    中国青年网  Tags:大数据杀熟   点击:(34)  评论:(0)  加入收藏
简易百科:到底什么是大数据?
随着互联网的快速发展,大数据已经成为了当今社会最热门的话题之一。那么,到底什么是大数据呢?首先,我们需要明确大数据的定义。大数据是指数据量极大、类型繁多、处理难度高的数...【详细内容】
2024-01-30    简易百科  Tags:大数据   点击:(40)  评论:(0)  加入收藏
数据采集新篇章:AI与大模型的融合应用
开篇在AIGC(人工智能与通用计算)应用中,大型语言模型(LLM)占据着举足轻重的地位。这些模型,如GPT和BERT系列,通过处理和分析庞大的数据集,已经极大地推动了自然语言理解和生成的边界...【详细内容】
2024-01-17  崔皓  51CTO  Tags:数据采集   点击:(52)  评论:(0)  加入收藏
挑战 Spark 和 Flink?大数据技术栈的突围和战争
十年的轮回,正如大数据的发展一般,它既是一个轮回的结束,也是崭新的起点。大数据在过去的二十年中蓬勃发展,从无到有,崛起为最具爆炸性的技术领域之一,逐渐演变成为每个企业不可或...【详细内容】
2024-01-17  InfoQ    Tags:大数据   点击:(40)  评论:(0)  加入收藏
分布式存储系统在大数据处理中扮演着怎样的角色?
如果存储节点本身可以定制,则通常会让其支持部分计算能力,以利用数据的亲和性,将部分计算下推到相关的存储节点上。如果存储是云上的 S3 等对象存储,无法定制,则通常会将数据在计...【详细内容】
2023-12-19  木鸟杂记  微信公众号  Tags:大数据   点击:(48)  评论:(0)  加入收藏
大数据如何实时拯救生命:车联网的数据分析有助预防交通事故
译者 | 李睿审校 | 重楼车联网(IoV)是汽车行业与物联网相结合的产物。预计车联网数据规模将越来越大,尤其是当电动汽车成为汽车市场新的增长引擎。问题是:用户的数据平台准备...【详细内容】
2023-12-19    51CTO  Tags:大数据   点击:(41)  评论:(0)  加入收藏
利用生成对抗网络进行匿名化数据处理
在互联网时代,数据日益成为人们的生产资料。然而,在某些情况下,我们需要分享数据,但又需要保护个人隐私。这时,匿名化技术就显得尤为重要。本文将介绍利用生成对抗网络进行匿名化...【详细内容】
2023-12-18  技巧达人小影    Tags:数据处理   点击:(57)  评论:(0)  加入收藏
盘点那些常见的数据中心类型,你知道几个?
在数字化潮流的浪潮下,数据中心如同企业的神经系统,关系到业务的稳健运转。而在这个巨大的网络中,各种数据中心类型如雨后春笋般崭露头角。从企业级的个性至云数据中心的虚拟化...【详细内容】
2023-12-07  数据中心之家  微信公众号  Tags:数据中心   点击:(67)  评论:(0)  加入收藏
数据中心的七个关键特征
随着信息技术的不断演进,数据中心的可靠性、可扩展性、高效性、安全性、灵活性、管理性和可持续性成为业界探讨的焦点。下面让我们一同深入剖析这些关键特征,了解它们是如何影...【详细内容】
2023-12-06  数据中心之家  微信公众号  Tags:数据   点击:(64)  评论:(0)  加入收藏
什么是数据解析?将数据转化为更好的决策
什么是数据解析?数据解析是一门专注于从数据中获取洞察力的学科。它包含数据分析(data analysis)和管理的流程、工具和技术,包括数据的收集、组织和存储。数据解析的主要目的是...【详细内容】
2023-12-06  计算机世界    Tags:数据解析   点击:(64)  评论:(0)  加入收藏
站内最新
站内热门
站内头条