一、传统方式上进行百万导出
根据前面我们的方式解决百万导出,那么就要做百万次循环
//1.构造数据,假设有百万数据
List list = userService.findByReport(companyId,month+"%");
//2.创建工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
//3.构造sheet
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);
int titleInext = 0;
Cell cell = null;
//4.写入单元格
for (user report : list) {
Row dataRow = sheet.createRow(titleInext++);
//编号
cell = dataRow.createCell(0);
cell.setCellValue(report.getUserId());
//姓名
cell = dataRow.createCell(1);
cell.setCellValue(report.getUsername());
//手机
cell = dataRow.createCell(2);
cell.setCellValue(report.getMobile());
}
如果是基于XSSFWorkbook报表导出,那么我们在Jvisualvm看看效率
使用传统的方式去处理百万数据,会不断的占用内存,直到吃满跑出oom异常
那么为什么会这样呢?
二、传统方式上问题分析
那么为什么会占用那么多内存呢?
首先我们row、cell单元格、cell样式等等都是一个对象
当我们excel导出而言,是将所有对象都创建出来放入内存中,所有的对象都加载完成后,在以流的方式进行加载下载出来
也就是说下载之前我们的对象一直在占有中,没有被释放,所以占用字节非常的大
对于百万数据量的Excel导入导出,只讨论基于Excel2007的解决方法。
在ApachePoi 官方提供了对操作大数据量的导入导出的工具和解决办法,操作Excel2007使用XSSF对象,可以分为三种模式:
- 用户模式:用户模式有许多封装好的方法操作简单,但
创建太多的对象,非常耗内存
(之前使用的方法) - 事件模式:
基于SAX方式解析XML
,SAX全称Simple API for XML,它是一个接口,也是一个软件包。它是一种XML解析的替代方法,不同于DOM解析XML文档时把所有内容一次性加载到内存中的方式,它逐行扫描文档,一边扫描,一边解析
。 - SXSSF对象:是用来生成海量excel数据文件,
主要原理是借助临时存储空间生成excel
Apache POI官方提供有一张图片,描述了基于用户模式,事件模式,以及使用SXSSF三种方式操作Excel的特性以及CUP和内存占用情况
三、解决思路分析
我们刚刚进行问题分析知道基于XSSFWork导出Excel报表,是通过将所有单元格对象保存到内存中,当所有的Excel单元格全部创建完成之后
一次性写入到Excel并导出。
当百万数据级别的Excel导出时,随着表格的不断创建,内存中对象越来越多,直至内存溢出。
以及我们说到Apache Poi提供了SXSSFWork对象,专门用于处理大数据量Excel报表导出。
那么为什么使用SXSSFWork对象就可以解决这个问题呢?
SXSSFWork原理分析
在实例化SXSSFWork这个对象时,可以指定在内存中所产生的POI导出相关对象的数量(默认100)。
一旦内存中的对象的个数达到这个指定值时,就将内存中的这些对象的内容写入到磁盘中(XML的文件格式)。
再将这些对象从内存中销毁,以后只要达到这个值,就会以类似的处理方式处理,直至Excel导出完成。
四、使用SXSSFWork优化传统方式
接下来我们使用SXSSFWork对象优化传统方式上的不足
//1.构造数据,假设有百万数据
List list = userService.findByReport(companyId,month+"%");
//2.创建工作簿
//可以在括号里写阈值默认为100,即内存中的对象数量最大数量
SXSSFWorkbook workbook = new SXSSFWorkbook();
//3.构造sheet
Sheet sheet = workbook.createSheet();
Cell cell = null;
//4.写入单元格
for (user report : list) {
Row dataRow = sheet.createRow(titleInext++);
//编号
cell = dataRow.createCell(0);
cell.setCellValue(report.getUserId());
//姓名
cell = dataRow.createCell(1);
cell.setCellValue(report.getUsername());
//手机
cell = dataRow.createCell(2);
cell.setCellValue(report.getMobile());
}
接下来我们使用SXSSFWorkbook生成Excel报表的方式看看Jvisualvm
这时我们发现蓝色的地方有点波折,这是因为将对象写入了磁盘里
所以会先上坡再下坡,但是他的效率相比传统方式占用字节少了很多很多
五、总结
当我们采用SXSSFWorkbook方式的时候会发现他会转为xml的临时方式进行放入硬盘文件
即使我们是通过临时文件的形式放入硬盘中,也会出现有撑爆的风险
因为我们是先放入内存中,再从内存中转入硬盘文件
对于传统的方式,我们可以采用SXSSFWorkbook来做,但它不是万能的还需要减少对象的产生,比如说样式、字体等
参考资料
黑马程序员:基于SaaS平台的iHRM刷脸登录实战开发(报表相关视频)