我所知道报表之POI百万数据导出测试、分析、原理、解决、总结

28640

一、传统方式上进行百万导出


根据前面我们的方式解决百万导出,那么就要做百万次循环

//1.构造数据,假设有百万数据
List<User> 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看看效率

image.png

使用传统的方式去处理百万数据,会不断的占用内存,直到吃满跑出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和内存占用情况

image.png

三、解决思路分析


我们刚刚进行问题分析知道基于XSSFWork导出Excel报表,是通过将所有单元格对象保存到内存中,当所有的Excel单元格全部创建完成之后一次性写入到Excel并导出。

当百万数据级别的Excel导出时,随着表格的不断创建,内存中对象越来越多,直至内存溢出。

以及我们说到Apache Poi提供了SXSSFWork对象,专门用于处理大数据量Excel报表导出。

那么为什么使用SXSSFWork对象就可以解决这个问题呢?

SXSSFWork原理分析

image.png

在实例化SXSSFWork这个对象时,可以指定在内存中所产生的POI导出相关对象的数量(默认100)。

一旦内存中的对象的个数达到这个指定值时,就将内存中的这些对象的内容写入到磁盘中(XML的文件格式)。

再将这些对象从内存中销毁,以后只要达到这个值,就会以类似的处理方式处理,直至Excel导出完成。

四、使用SXSSFWork优化传统方式


接下来我们使用SXSSFWork对象优化传统方式上的不足

//1.构造数据,假设有百万数据
List<User> 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

image.png

这时我们发现蓝色的地方有点波折,这是因为将对象写入了磁盘里

所以会先上坡再下坡,但是他的效率相比传统方式占用字节少了很多很多

五、总结


image.png

当我们采用SXSSFWorkbook方式的时候会发现他会转为xml的临时方式进行放入硬盘文件

image.png

即使我们是通过临时文件的形式放入硬盘中,也会出现有撑爆的风险

因为我们是先放入内存中,再从内存中转入硬盘文件

对于传统的方式,我们可以采用SXSSFWorkbook来做,但它不是万能的还需要减少对象的产生,比如说样式、字体等

参考资料


黑马程序员:基于SaaS平台的iHRM刷脸登录实战开发(报表相关视频)

阅读 718

心有多大,舞台就有多大

95 声望
20 粉丝
0 条评论
你知道吗?

心有多大,舞台就有多大

95 声望
20 粉丝
宣传栏