Excel大数据导入导出

Excel大数据导入导出

在Java中我们导入导出Excel一般使用的是Apache POI,很多开源工具也都是基于POI的。但是POI的使用不当可能会造成线上问题,比如数据量大的时候会造成Full GC或者OOM。

image-20210405112643148

从上图中可以看到POI相关的对象以及相关的XML对象占用了大量的内存空间,而且造成了频繁的

Full GC,但是却一直没有被回收。原因是导出的数据比较大,在导出结束前内存中有大量的Row,Cell,Style等没有被释放。

Excel存储格式

XLS

03版的XLS采用的是一种名为BIFF8(Binary-Interchange-File-Format),基于OLE2规范的二进制文件格式。大家也没必要去了解它,已经被淘汰了。

XLSX

07版的XLSX则是采用OOXML(Office Open Xml)的格式存储数据。简单来说就是一堆xml文件用zip打包之后文件。这个对于大家来说就熟悉了,把xlsx文件后缀名改为zip后,再解压出来就可以看到文件结构。

image-20210405114001571

image-20210405114024397

导出优化

由于xlsx底层使用xml存储,占用内存会比较大,官方也意识到这个问题,在3.8版本之后,提供了SXSSFWorkbook来优化写性能。

使用
1
Workbook workbook = new XSSFWorkbook(inputStream);

改成如下代码即可。

1
Workbook workbook = new SXSSFWorkbook(new XSSFWorkbook(inputStream));
原理

其原理是可以定义一个window size(默认100),生成Excel期间只在内存维持window size那么多的行数Row,超时window size时会把之前行Row写到一个临时文件并且remove释放掉,这样就可以达到释放内存的效果。 SXSSFSheet在创建Row时会判断并刷盘、释放超过window size的Row。

image-20210405114355847

image-20210405114510893

我们可以看到 SXSSFSheet在创建的时候,都会创建一个SheetDataWriter,刷盘动作正是由这个类完成的,调用 writeRow 方法。

image-20210405114621077

导入优化

用户模式(UserModel)

用户模式(User Model)就类似于dom方式的解析,是一种high level api,给人快速、方便开发用的。缺点是一次性将文件读入内存,构建一颗Dom树。并且在POI对Excel的抽象中,每一行,每一个单元格都是一个对象。当文件大,数据量多的时候对内存的占用可想而知。 用户模式就是类似用 WorkbookFactory.create(inputStream),poi 会把整个文件一次性解析,生成全部的Sheet,Row,Cell以及对象,如果导入文件数据量大的话,也很可能会导致OOM。

事件模式(EventModel)

事件模式(Event Model)就是SAX解析。Event Model使用的方式是边读取边解析,并且不会将这些数据封装成Row,Cell这样的对象。而都只是普通的数字或者是字符串。并且这些解析出来的对象是不需要一直驻留在内存中,而是解析完使用后就可以回收。所以相比于User Model,Event Model更节省内存,效率也更。但是作为代价,相比User Model功能更少,门槛也要高一些。我们需要去学习Excel存储数据的各个Xml中每个标签,标签中的属性的含义,然后对解析代码进行设计。

用户事件模式(User Event Model)

User Event Model也是采用流式解析,但是不同于Event Model,POI基于Event Model为我们封装了一层。我们不再面对Element的事件编程,而是面向StartRow,EndRow,Cell等事件编程。而提供的数据,也不再像之前是原始数据,而是全部格式化好,方便开发者开箱即用。大大简化了我们的开发效率。

XLSX

POI对XLSX支持Event Model和Event User Model。

官方例子简单来说就是继承XSSFSheetXMLHandler.SheetContentsHandler,覆盖其startRow,endRow,cell,endSheet 等方法。POI每开始读行,结束读行,读取一个cell,结束读取一个sheet时回调的方法。从方法名上看Event User Model有更好的用户体验。

XLS

POI对XLS支持Event Model。

需要继承HSSFListener,覆盖processRecord 方法,POI每读取到一个单元格的数据则会回调次方法。

参考

  1. Excel 大批量数据的导入和导出,如何做优化?
显示评论