700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > springboot+poi导出百万级数据避免OOM内存溢出

springboot+poi导出百万级数据避免OOM内存溢出

时间:2022-05-08 21:12:21

相关推荐

springboot+poi导出百万级数据避免OOM内存溢出

springboot+poi导出百万级数据避免OOM内存溢出

文章目录

springboot+poi导出百万级数据避免OOM内存溢出前言一、具体实现二、代码实现1.引入poi包2.功能代码 总结

前言

记录下在实际项目中碰到过一个重大问题,全量导出百万级数据,由于之前在开发环境数据没有达到百万级别,因此此问题没有处理,上到测试环境后,数据量达到150万的时候,全量导出所有的数据出现内存溢出问题。

一、具体实现

解决大数据量导出问题最关键点的在于释放内存,减少内存的消耗,避免出现OOM内存溢出问题。具体的操作为使用SXSSFWorkbook来解析数据,确保每次内存中的数据量保持在1000或者100左右,这样把其他的数据从内存中写入磁盘,以牺牲磁盘空间来释放内存,避免OOM。

二、代码实现

1.引入poi包

<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.0.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.0.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-full</artifactId><version>5.0.0</version></dependency>

2.功能代码

/*** 处理excel数据** @param language* @return*/@Overridepublic SXSSFWorkbook makeSXSSFWorkbook() {Long startTime = System.currentTimeMillis();// 获取总数据量Query queryAll = new Query();Long allCount = mongoTemplate.count(queryAll, CdrEntity.class);if (allCount <= 0) {return null;}// 分页参数-每页支持10000条记录int pageSize = 10000;// 记录需要循环的sheet页次数int loopCount = 0;// 由于Excel的一张sheet允许的最大行数是 1048575,所以当数据量超过 1048575 行时,需要增加 sheet 页签 -> 这里取1000000条做为一个sheet页的最大值int sheetMax = 1000000;if (allCount > sheetMax) {loopCount = allCount.intValue() / sheetMax;}logger.info("数据量: " + allCount);// 这样表示SXSSFWorkbook只会保留100条数据在内存中,其它的数据都会写到磁盘里,这样的话占用的内存就会很少int rowAccessWindowSize = 100;SXSSFWorkbook workbook = new SXSSFWorkbook(rowAccessWindowSize);// 记录当前页的总数int tempMax = 0;// 记录当前sheet页导出的数据量int tgMax = 0;for (int m = 0; m <= loopCount; m++) {SXSSFSheet sheet = workbook.createSheet("sheet" + (m + 1));logger.info("================写入第 " + (m + 1) + " 个sheet页================");String[] headers = new String[]{"呼叫类型", "话单类型", "主叫号码"};// 设置列名Row row = sheet.createRow((short) 0);for (int i = 0; i < headers.length; i++) {Cell cell = row.createCell(i);cell.setCellValue(headers[i]);}// page:查询总数据量为 allCount条记录时,每次生成pageSize条记录 需要 page 次进行导出int maxAll = (int) (allCount - (m * sheetMax));int page = maxAll / pageSize;logger.info("第 " +(m + 1)+ " 个sheet页循环->page值为: " + page);for (int i = 0; i <= page; i++) {logger.info("第" + (i + 1) + "次循环开始...");int startIndex = i * pageSize;logger.info("导出分页:起始位置(startIndex) -> " + (tgMax + startIndex) + ", 每页数据量(pageSize) -> " + pageSize);// 查询数据Query queryData = new Query();queryData.skip((tgMax + startIndex));queryData.limit(pageSize);List<CdrEntity> dataList = mongoTemplate.find(queryData, CdrEntity.class);if (CollectionUtils.isNotEmpty(dataList)) {for (int rowNum = 1, len = dataList.size() + 1; rowNum < len; rowNum++) {Row row1 = sheet.createRow(startIndex + rowNum);CdrEntity cdrEntity = dataList.get(rowNum - 1);RecordVo recordVo = new RecordVo();// 呼叫类型recordVo.setCallType(CallTypeEnum.getNameByCode(String.valueOf(cdrEntity.getnCallType()), language));Cell cell1 = row1.createCell(0);cell1.setCellValue(recordVo.getCallType());// 话单类型recordVo.setCdrType(BillTypeEnum.getNameByCode(String.valueOf(cdrEntity.getnCdrType()), language));Cell cell2 = row1.createCell(1);cell2.setCellValue(recordVo.getCdrType());// 主叫号码recordVo.setCallerNum(cdrEntity.getStrCallerNum());Cell cell3 = row1.createCell(2);cell3.setCellValue(recordVo.getCallerNum());if (rowNum % rowAccessWindowSize == 0) {try {// 数据写入磁盘,释放内存sheet.flushRows();} catch (IOException e) {logger.error("sheet从内存写入本地硬盘失败", e);}}}}tempMax = (i + 1) * pageSize;// 当达到一个sheet页数据量最大值时,跳出当次循环,继续循环下一个sheet数据if (tempMax >= sheetMax) {tgMax = (m + 1) * sheetMax;logger.info("tgMax值: " + tgMax);logger.info("结束第 " + (m + 1) + " 个sheet页循环...");break;}}}Long endTime = System.currentTimeMillis();logger.info("消耗时间: " + (endTime - startTime));return workbook;}

/*** 导出操作** @param request* @param response* @param workbook* @param language* @param systemType* @return*/@Overridepublic boolean exportExcel(HttpServletRequest request, HttpServletResponse response, SXSSFWorkbook workbook) {OutputStream out = null;String fileName = "test.xlsx";try {out = response.getOutputStream();response.setContentType("application/x-download");response.setHeader("Pragma", "public");response.setHeader("Cache-Control", "max-age=30");response.setHeader("Content-disposition", "attachment;filename=" + fileName);workbook.write(out);out.flush();} catch (Exception e) {logger.error("导出Excel文件失败", e);return false;} finally {if (out != null) {try {out.close();} catch (IOException e) {logger.error("导出Excel文件关闭输出流失败", e);} finally {out = null;}}if (workbook != null) {try {workbook.dispose();workbook.close();} catch (IOException e) {logger.error("导出Excel文件关闭输出流失败", e);} finally {workbook = null;}}}return true;}

总结

这段代码亲测有效,到测试环境上导出五百多万条数据都没有出现OOM问题,希望对各位朋友有效。这里实现了多个sheet页导出的数据,excel导出一个sheet最多能导出100万多一点数据,至于具体数字,请朋友们自行百度下。谢谢大家

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。