java百万千万级别excel导出问题(导出慢和Out Of Memory内存溢出)

createh55个月前 (02-01)技术教程47

目录

业务场景

原因分析

解决思路

优化后效果

实现代码DEMO

业务场景

由于很多业务需要导出数据库里的数据,一般我们导出的数据都是要给业务部门看的,他们也会拿到做一些数据统计,所以一般都是给他们导出Excel格式的数据文件,但是当我导出五十万条数据时遇到了两个问题:


导出时一般使用POI工具包,这时很容易导致内存溢出

导出时间很慢,很容易导致超时

下面是直接导出时内存占用.

原因分析

由于Java对象是封装型对象,所以内存中对象大小是实际数据的好几倍,所以50W条的数据,最少要有50W个对象,再加上我们可能使用map对象或者JsonObject对其进行一些数据操作,所以内存中保守会有100W个对象,可能要占用至少2G的内存大小。

POI工具为了加快速度,采用把文件全部读到内存中的形式操作文件,这导致如果要操作一个500M的Excel文件将至少占用500M的内存。

这导致内存维持在一个很高的水平


解决思路

1.减少内存中存在的封装类型对象个数

第一,这个我们可以使用分页查询的形式进行查询;

第二我们尽量使用String和int类型存储数据

2.替换POI操作文件形式,使用文件流形式写数据,但是Excel是有严格格式的文件,直接采用文本形式写入流根本无法用Excel形式打开,这时候我们想到了一个文件格式,那就是 csv格式的文件,本质上是文本格式,但是可以用Excel形式打开,并保存为Excel格式。于是我们测试了一下


优化后效果

当每次查询1000条,每次查出来这1000条往文件写一次,查询1000次时内存变化如下:

可以看出内存最多占用350M,不会再往上增加,但是分页次数太多,所以整体时间太长


当每次查询50000条,每一条写一次时内存变化如下:

可以看到内存涨到800M时就不会再增加了,我们可以适当的调整每页的大小,直到我们能够接受的内存大小和总体时间就可以了。


另外写入文件和数据库查询是IO操作,耗时的操作,所以我们也可以通过判断,在适合的时机查询和写入,适当的减少写入次数来提升整体速度。平衡时间和内存的使用,一般时间使用短了,内存使用就会变大。


实现代码DEMO
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:cn/xdf/wlyyb/spring-context.xml"})
public class TestExportLog {




  @Autowired
  private CheckInOutDaoTest checkInOutDaoTest;
  @Test
  public void export() throws IOException {
    long startTime = System.currentTimeMillis();
    File file = new File("e:/log.csv");
    if (!file.exists()) {
      file.createNewFile();
    }else {
      file.delete();
      file.createNewFile();
    }
    //声明文件流
    FileWriterWithEncoding writer = new FileWriterWithEncoding(file, "gbk");
    StringBuffer content = new StringBuffer();
    for (int i = 1; i < 1000; i++) {
      PageUntil page = new PageUntil();
      //通过页大小调整查询次数
      page.setPageSize(100000);
      page.setPageNum(i);
      List  logList = checkInOutDaoTest.getListByPage( page);
      for (int j = 0; j < logList.size(); j++) {
        CheckInOut logs = logList.get(j);
        content.append(logs.getCheckDate()+","+logs.getChecktime()+","+logs.getCreat_time()+","+logs.getSn()+","+
              logs.getUid()+","+logs.getUserCode()+","+logs.getMachineid()+","+logs.getSensorid()+",很长很长的文本很长很长的文本很长很长的文本很长很长的文本很长很长的文本很长很长的文本很长很长的文本很长很长的文本很长很长的文本"+"\r\n");
        //文本大小达到一定长度,往硬盘上写一次,清空其内存占用
        if (content.length()> 3*1024*1024) {
          //追加文本内容
          writer.write(content.toString());
          content = new StringBuffer();
        }


      }
//      System.out.println("第"+i+"页"+content);
      System.out.println("第"+i+"页");
      if (StringUtils.isBlank(content)) {
        break;
      }
    }
    追加文本内容
    writer.write(content.toString());




    //关闭流
    writer.close();




    System.out.println("用时:"+(System.currentTimeMillis()-startTime));
  }
}



相关文章

Hutool Java工具类库导出Excel,超级简单

作者:程序猿的内心独白原文链接:http://suo.im/5Zxx2L前言在开发应用系统的时候,导出文件是必不可放的功能。以前用过POI、easyexcel等工具的导入导出功能,但总感觉太麻烦了,代...

java将百万级别数据导出到Excel中,用时仅需要98941毫秒

程序员创业记跟大家分享一款Excel组件,之所以分享这款,是因为它在处理excel时很方便,我将百万数据导出到excel,耗时仅不用两分钟。poi概述Apache POI是Apache软件基金会的开放...

盘点三种Excel转SHP文件的方法(附练习数据下载)

概述数据的获取渠道是多种多样的,获取的数据格式也是多种多样,作为一名GISer,需要熟练掌握各种格式的数据之间的转换,例如本文要介绍的Excel格式的数据,经常会遇到,如果需要转换为SHP格式应该怎么...

用的处理excel导入导出的Java类库和工具包有哪些

常用的处理excel导入导出的Java类库和工具包有以下几种:Apache POI:一款流行的用于读写Microsoft Office格式文件的Java库。它能够读写Excel、Word和PowerP...

SpringBoot 实现 Excel 导入导出,性能爆表,用起来够优雅

操作Excel实现导入导出是个非常常见的需求,之前介绍了一款非常好用的工具EasyPoi。有读者提出在数据量大的情况下,EasyPoi占用内存大,性能不够好。今天给大家推荐一款性能更好的Excel导入...

多人同时导出 Excel 干崩服务器!参考阿里大佬给出的解决方案

前言业务诉求:考虑到数据库数据日渐增多,导出会有全量数据的导出,多人同时导出可以会对服务性能造成影响,导出涉及到mysql查询的io操作,还涉及文件输入、输出流的io操作,所以对服务器的性能会影响的比...