做网站开发需要学什么软件,网站设计所用到的技术,网站基本建设,wordpress 设置密码前言 最近有个项目在生产环境做数据导入时#xff0c;发现开始执行导入任务会出现cpu狂飙的情况。几番定位查找发现是在读取excel的时候导致此问题的发生#xff0c;因此在通常使用的为POI的普通读取#xff0c;在遇到大数据量excel#xff0c;50MB大小或数五十万行的级别的…前言 最近有个项目在生产环境做数据导入时发现开始执行导入任务会出现cpu狂飙的情况。几番定位查找发现是在读取excel的时候导致此问题的发生因此在通常使用的为POI的普通读取在遇到大数据量excel50MB大小或数五十万行的级别的数据容易导致读取时内存溢出或者cpu飙升。需要注意本文讨论的是针对xlsx格式的excel文件上传。
关于Excel相关技术 在Java技术生态圈中可以进行Excel处理的主流技术包括Apache POIJXLAlibaba EasyExcel等。由于JXL只支持Excel2003以下版本所以不太常见。 Apache POI基于DOM方式进行解析将文件直接加载内存所以速度较快适合Excel文件数量不大的应用场景 Alibaba EasyExcel:采用逐行读取的解析模式将每一行的解析结果以观察者模式通知处理AnalyEventListener所以比较适合数据体量较大的Excel文件解析。 问题代码 这种方式POI会把文件的所有内容都加载到内存中读取大的excel文件时很容易占用大量内存导致oom的发生全部文件加载如下 /*** POI方式读取excel** param file*/public static void readExcelByPoi(File file) {long start System.currentTimeMillis();//整个文件都一块载入try (InputStream inp new FileInputStream(file);Workbook wb WorkbookFactory.create(inp)) {log.info(读取excel完毕耗时{}毫秒, System.currentTimeMillis() - start);Sheet sheet wb.getSheetAt(0);//更新总数System.out.println(读取结束行数 sheet.getLastRowNum());} catch (Exception e) {e.printStackTrace();}}当前引入的poi依赖 !-- excel工具 --dependencygroupIdorg.apache.poi/groupIdartifactIdpoi-ooxml/artifactIdversion4.1.0/version/dependency 读取50MB我本地字段不是很多50万行数据 首先在读取excel文件的断点执行之前的cpu和内存的占用分别为50%和42%上传的excel大小为50MB这里我就不一一带大家测试了以上此种方式肯定是行不通的。
解决方案一xlsx-streamer 我们采用分段缓存的方式加载数据到内存中此种方式在创建Workbook对象时借助xlsx-streamerStreamingReader 来创建一个缓冲区域批量地读取文件 因此不会将整个文件实例化到对象当中代码如下
引入依赖 !-- excel工具 --dependencygroupIdorg.apache.poi/groupIdartifactIdpoi-ooxml/artifactIdversion4.1.0/version/dependency!-- 读取大量excel数据时使用 --dependencygroupIdcom.monitorjbl/groupIdartifactIdxlsx-streamer/artifactIdversion2.1.0/version/dependency示例代码 /*** 大批量数据读取 十万级以上* 思路采用分段缓存加载数据防止出现OOM的情况** param file* throws Exception*/public static void readLagerExcel(File file) throws Exception {InputStream inputStream new FileInputStream(file);long start System.currentTimeMillis();try (Workbook workbook StreamingReader.builder().rowCacheSize(10 * 10) //缓存到内存中的行数默认是10.bufferSize(1024 * 4) //读取资源时缓存到内存的字节大小默认是1024.open(inputStream)) { //打开资源可以是InputStream或者是File注意只能打开.xlsx格式的文件Sheet sheet workbook.getSheetAt(0);log.info(读取excel完毕耗时{}毫秒, System.currentTimeMillis() - start);//遍历所有的行for (Row row : sheet) {System.out.println(开始遍历第 row.getRowNum() 行数据);//遍历所有的列for (Cell cell : row) {System.out.print(cell.getStringCellValue() );}System.out.println( );}//总数System.out.println(读取结束行数 sheet.getLastRowNum());}}加载结果 40万级别数据近花费5秒加载是不是很快了。
百万级别也就花费7秒
前端也还做了个测试页面如下 Excel文件上传 解决方案二EasyExcel 使用EasyExcel解决大文件Excel内存溢出的问题基于POI进行封装优化可以在不考虑性能、内存的等因素的情况下快速完成Excel的读、写等功能。 官网 https://easyexcel.opensource.alibaba.com/ githubhttps://github.com/alibaba/easyexcel 引入依赖 !--easyExcel工具--dependencygroupIdcom.alibaba/groupIdartifactIdeasyexcel/artifactIdversion3.3.1/version/dependency
示例代码 仅做简单读取示例: /*** EasyExcel方式读取excel* 读取并封装为对象,ExcelData大家需要的对象* param file*/public static void readExcelByEasyExcel(File file) {long start System.currentTimeMillis();ListExcelData excelDataList EasyExcel.read(file).head(ExcelData.class).sheet(0).doReadSync();excelDataList.stream().forEach(x - System.out.println(x.toString()));log.info(读取excel完毕耗时{}毫秒, System.currentTimeMillis() - start);}/*** EasyExcel方式读取excel* 不指定head类* param file*/public static void readExcelByEasyExcel1(File file) {long start System.currentTimeMillis();ListMapInteger, String listMap EasyExcel.read(file).sheet(0).doReadSync();listMap.stream().forEach(x - System.out.println(JSON.toJSONString(x)));log.info(读取excel完毕耗时{}毫秒, System.currentTimeMillis() - start);}得出一个结论就是使用阿里EasyExcel确实方便很多不仅支持excelcsv也可以。支持的文件类型更多些但是第一种方式也还可以毕竟poi我们也一直在使用。