实测好用 EasyExcel带格式多线程导出百万数据

前言
以下为结合实际情况作的方案设计,导出阈值以及单sheet页条数都可以根据实际情况调整
大佬可直接跳过新手教程,直接查看文末代码
背景说明
针对明细报表,用户会选择针对当前明细数据进行导出,便于本地或者线下进行处理或者计算等需求 。不过一般在这种大数据量的导出任务下,会引发以下问题:
 

  • 响应时间较慢;
  • 内存资源占用过大,基本上一个大数据量的导出会消耗可视化服务的所有资源,引起内存回收,其它接口无响应;
     
考虑到单个Excel文件过大,采用压缩文件流zip的方式,一个excel只有一个页签,一个页签最多十万条数据,所以少于十万条数据,会导出excel文件,而非zip压缩文件 。
另外,这里导出功能的速率不能单以数据条数为量级进行衡量,平常一般一万条数据就是1M字节 。较为准确的公式如下(借此就可以评估出很多数据导出的文件大小):
 
文件大小1M字节 = 字段列数15个 * 数据条数一万条
方案概述 1)大数据量导出问题主要是以下三个地方:
  • 资源占用
  • 内存(也是资源的一个,单独说明)
  • 响应时间
针对以上三个问题,大方向考虑的是多线程结合数据流写入的方式 。多线程:使用空间换时间,主要是加快接口响应时间,但是这里线程数不宜过多,一味加快响应时间提升线程数,资源占用会非常严重,故会考虑线程池,线程池的线程数为10;数据流:数据的IO-读取/写入等操作一般都是通过“数据包”的方式,即将结果数据作为一个整体,这样如果数据量多的话,会非常占用内存,所以采用数据流的方式,而且导出的时候会进行格式设置(单元格合并、背景色、字体样式等),一直使用的是Alibaba EasyExcel组件,并且Alibaba EasyExcel组件支持数据流的方式读取/写入数据 。
2)将写入导出Excel等功能单独分开成一个微服务:
注意:如果单个服务分配的资源足够的话,可以不用将导出功能与原应用服务拆开,这里可以省略
 
  • 抢占资源
  • 由于导出功能内存溢出,如果不做分离独立,整个应用服务也会宕机
  • JAVA技术栈路线:https://www.yoodb.com/
     
3)注意:
  • 多线程下,同一页签的写入不可同步,即Alibaba EasyExcel组件的文件写入流SheetWriter是异步的;
  • 多线程下,每个线程所用的文件写入SheetWriter是一个复制,依旧会占用大量内存;
  • 微服务拆分时,数据读取和文件写入是在一个线程下的,所以新的微服务也要实现一套数据读取逻辑;
  • 压缩文件使用压缩文件流,ZipOutputStream,不需要暂存本地;
     
4)方案设计: 
实测好用 EasyExcel带格式多线程导出百万数据

文章插图
标注说明
1) 阈值可以进行设置,考虑到业务场景以及资源使用,这里阈值数据量为100w条,超过一百万会导出空表(而非导出一百万数据)
2) 导出进行多线程,启用最多十个多线程(默认最多一百万条数据,一个sheet页十万条数据),每个线程会进行两个动作,查询数据以及数据写入操作,(如果数据量较少,依旧是适用的)
3) 说明图,以86万数据为例,也就是说会启用九个文件写入线程,一个文件写入线程生成一个excel导出文件;
实测好用 EasyExcel带格式多线程导出百万数据

文章插图
4) 线程池为队列线程,即后来的线程进入排队等待,队列长度(线程池大小)为10;
5) 每个文件写入线程会生成最多十个sheet(默认一个sheet页十万数据)写入线程(最后一个文件写入线程可能会少于十个) 。
实测好用 EasyExcel带格式多线程导出百万数据

文章插图
5)maven依赖:
com.alibabagroupId>
easyexcelartifactId>
2.1.7version>
dependency>

详细设计
1)文件写入多线程,按每个文件十万条数据进行导出,每个文件写入线程生成一个excel文件(单页签);