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

createh53个月前 (02-01)技术教程25

目录

业务场景

原因分析

解决思路

优化后效果

实现代码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导出功能?

Java作为一种常用的编程语言,在实现大量数据导出功能时具有很高的效率和可扩展性。本文将介绍如何使用Java实现百万数据的Excel导出功能。一、需求分析在很多实际应用场景中,我们需要将大量数据导出到...

程序员:超级简单导出Excel 工具,Hutool Java工具类库

前言在开发应用系统的时候,导出文件是必不可放的功能。以前用过POI、easyexcel等工具的导入导出功能,但总感觉太麻烦了,代码特别多,感觉并不是很好用。今天给大家介绍一款新工具,java工具类库H...

原来用hutool导入导出Excel这么丝滑!

1. hutool 工具介绍unset这个我就不用过多介绍了,它是一个非常好用的 Java 工具类库。我们在日常工作中用到的工具类,他都有。官网:https://plus.hutool.cn/ uns...

java bean 一对多,多对一 poi导出excel表格

最近造了个poi导出 excel轮子特点java bean 一对多、多对一关系合并单元行支持图片导出Bean 一对多关系合并行代码public class User { @Excel(nam...