0
点赞
收藏
分享

微信扫一扫

100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇


100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇_数据导出


悟纤:师傅,这回真的玩脱了。

师傅:怎么说,你又搞啥了见不得人的的事情。

100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇_List_02


悟纤:你这脑洞现在可以了,什么叫见不得人的事情。

100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇_easypoi_03

师傅:那现在还有事情可以难得住你呐。

悟纤:这一次夸大了,产品让我把数据库100万的用户数据导出,我以为会很顺呢,结果导出半天,数据没导出,程序直接就炸了GC overhead limit exceeded。

师傅:这个大数据的导出,是不能用普通的导出数据的方式的,今天师傅就给你放个大招。

悟纤:师傅,你真是我的救世主,在我最困难的时候,总能来救助我。

师傅:好话就不用说了,解决问题之后,请我去吃一顿好的。

悟纤:师傅,你说这个是事情吗,别说是一顿,十顿饭都不成问题。

悟纤:问题搞起,饭饭搞起~

100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇_springboot_04

导读

Hi,大家好,我是悟纤

我就是我,不一样的烟火。我就是我,与众不同的小苹果。

大数据导出是当我们的导出数量在几万,到上百万的数据时,一次从数据库查询这么多数据加载到内存然后写入会对我们的内存和CPU都产生压力,这个时候需要我们像分页一样处理导出分段写入Excel缓解Excel的压力。


一、问题的提出

产品:悟纤,将数据库100万的用户信息导出一下~

悟纤:好的,马上。

这不开发好的导出功能吗,这个还不简单么,今天绝对不能加班了。

于是,我写了这么一段代码:

/**
 * 大数据导出1.0
 * /demo/exportExcel4
 * @param response
 */
@GetMapping("/exportExcel4")
public void exportExcel4(HttpServletResponse response) throws IOException {

    Date start = new Date();

    // 模拟数据
    List<UserExportVO> users = new ArrayList<>();
    for (int i = 0; i < 1000000; i++) {  //一百万数据量
        users.add(new UserExportVO("悟纤-"+i,1,new Date(),"18688888888","abc"+i+"@qq.com",null,"公众号SpringBoot"));
    }

    ExcelUtil.exportExcelX(users, "测试导出表", "sheet1", UserExportVO.class, "测试导出表.xlsx", response);
    System.out.println("耗时:"+(new Date().getTime() - start.getTime())/1000+"秒");
}

能否导出?能,导出耗时:115秒,导出文件大小:157M。

二、大数据导出

导出是能够与导出了,但是耗时太长了,有办法减半吗?导出文件大小太大了,能办法减半吗?

必须的,这就是这节要介绍的,大数据导出。

大数据导出是当我们的导出数量在几万,到上百万的数据时,一次从数据库查询这么多数据加载到内存然后写入会对我们的内存CPU都产生压力,这个时候需要我们像分页一样处理导出分段写入Excel缓解Excel的压力。

大数据导出主要是使用到了ExcelExportUtil.exportBigExcel的方法:

100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇_easypoi_05

欧了,那就来写个小栗子,看看效果如何:

/**
 * 大数据导出2.0
 * /demo/exportExcel5
 * @param response
 */
@GetMapping("/exportExcel5")
public void exportExcel5(HttpServletResponse response) throws IOException {

    Date start = new Date();
    Workbook workbook = null;
    ExportParams params = new ExportParams("大数据测试", "测试");

    workbook = ExcelExportUtil.exportBigExcel(params, UserExportVO.class, new IExcelExportServer() {

        @Override
        public List<Object> selectListForExcelExport(Object obj, int page) {
            if (((int) obj) == page) {
                return null;
            }
            List<Object> list = new ArrayList<Object>();
            for (int i = 0; i < 10000; i++) {//1页查询1万,总共100页,100万数据.
                list.add(new UserExportVO("悟纤-"+i,1,new Date(),"18688888888","abc"+i+"@qq.com",null,"公众号SpringBoot"));
            }
            return list;
        }
    }, 100);
    ExcelUtil.downLoadExcel("大数据导出测试.xlsx",response,workbook);
    System.out.println("耗时:"+(new Date().getTime() - start.getTime())/1000+"秒");
}

这里的最难的地方,就是接口IExcelExportServer的实现,底层会进行page++,不断的查找下一页,所以这里一定要有一段结束这个循环的逻辑。在实际项目中,更多的是查询的list的size()==0了。

page是从1开始的:

100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇_springboot_06

Ok,来看下导出的时间和导出的大小。

导出时间60秒左右,和刚刚的115秒,时间几乎少了一半。

100万数据导出,居然爆炸了OutOfMemoryError?【EasyPoi实战系列】- 第472篇_数据_07

导出的文件的大小,从原来的157M,变为了28M,少了6倍左右。

当然这个第一次的导出方式,文件的大小,也和导出的配置有关系,在之前为了解决图片导出问题,设置了为ExcelType.HSSF。

如果设置为ExcelType.XSSF的格式直接就OutOfMemoryError: GC overhead limit exceeded了(这种情况发生的原因是,程序基本上耗尽了所有的可用内存, GC也清理不了)。

总结

对于大数据的导出,核心要注意的就是内存溢出了。

(1)100万的数据,使用ExcelType.XSSF的方式导出,会报错:OutOfMemoryError: GC overhead limit exceeded。

(2)100万的数据,使用ExcelType. HSSF的方式导出,能导出,耗时115秒左右,导出的文件大小157M左右。

(3)大数据的导出方式,能导出,耗时60秒左右,导出的文件大小28M左右。

我就是我,是颜色不一样的烟火。
我就是我,是与众不同的小苹果。

à悟纤学院:


举报

相关推荐

java100万数据量导出测试1

0 条评论