Spring Boot Excel导出三

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

前言

上一章讲解了easyexcel导出excel基本功能,不熟悉的朋友可以查看 juejin.cn/post/708274… 但是在实际的项目中会遇到百万级的的Excel导出,本文将讲解大数据量Excel如何高性能导出。

案例分析

之前的项目有个需求需要导出一个月的订单数据,数据量大约为100万左右,新同事的采用了如下的方法实现,相关数据库操作进行了省略,提取了核心实现逻辑。

 public static void exportBigData()
    {
        System.out.println("start export");
        long startTime=System.currentTimeMillis();
        //查询数据库获得
        List<TUser> userList =getUserPagedList(1000000);
        //数据大小
        int totalCount=userList.size();
        //每个excel导出10万行
        int pageSize=100000;
        //需要多少excel
        int totalPage=totalCount%pageSize==0?totalCount/pageSize:totalCount/pageSize+1;
        //开始位置
        int startRow=0;
        for(int i=1;i<=totalPage;i++)
        {
            String fileName = "E:\\easyExcel\\excel"+i+".xlsx";
            ExcelWriter excelWriter = null;
            try
            {
                excelWriter = EasyExcel.write(fileName, TUser.class).build();
                WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
                //通过内存分页导出数据
                int fromIndex=startRow;
                int endIndex = startRow+pageSize;
                if(endIndex>totalCount)
                {
                   excelWriter.write(userList.subList(fromIndex, totalCount), writeSheet);
                }
                else
                {
                    excelWriter.write(userList.subList(fromIndex, endIndex), writeSheet);
                }
                //
                startRow = startRow+pageSize;
            }
            finally
            {
                if (excelWriter != null) 
                {
                    excelWriter.finish();
                }
            }
        }
        
        System.out.println("end export...");
        System.out.println("export cost time:"+(System.currentTimeMillis()-startTime));
    }
复制代码

系统刚开始运行导出excel没出问题,当系统的并发量提升后,多人同时导出excel则出现了内存溢出的异常。

问题分析

上述代码存在如下问题:

1.该方法是通过查询一次性将所有的数据加载到内存中,如果是多人同时操作,那么会将系统的内存很快会占用完。

2.分页的实现采用的是java的subList来实现java的内存分页,内存分页的性能和效率比较低

3.操作完成后没有将查询对象的内存数据进行释放,这样导致用户多导几次excel,内存会持续的新增。

解决方案

针对大数据的excel导出,我们需要从几个方面来提升性能

1.数据库性能

查询数据的时需要考虑通过条件查询数据,查询条件是否有添加对应的索引。

2.数据库连接池性能,

数据库的连接池是否进行了调优来满足相应的需求

3.java程序代码性能

采用数据库分页的方式来查询数据,分批导出。 excel数据对象导出完成后,需要释放内存。

通过问题分析和讲解后,同事进行了修改后的代码如下:

 public static void exportBigData2()
    {
        System.out.println("start export");
        long startTime=System.currentTimeMillis();
        //查询数据总数量
        int totalCount=getTotalUserCount();
        //每个excel导出10万行
        int pageSize=100000;
        //需要多少excel
        int totalPage=totalCount%pageSize==0?totalCount/pageSize:totalCount/pageSize+1;
        for(int i=1;i<=totalPage;i++)
        {
            String fileName = "E:\\easyExcel\\excel"+i+".xlsx";
            ExcelWriter excelWriter = null;
            try
            {
                excelWriter = EasyExcel.write(fileName, TUser.class).build();
                WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
                //通过分页多次查询
                PageHelper.startPage(i, pageSize);
                List<TUser> userList =getUserPagedList(pageSize);
                excelWriter.write(userList, writeSheet);
                //释放内存
                userList.clear();
            }
            finally
            {
                if (excelWriter != null) 
                {
                    excelWriter.finish();
                }
            }
        }
复制代码

解决方案的1,2两点通过优化数据库来提升性能,后续再重点讲解,第3点采用了数据库分页的方式进行了优化。经过同事的优化,基本上已经可以满足需求。

为什么没有用多对线程来导出excel

这个问题之前有考虑,经过相关测试的发现,easyexcel多线程同时往一个sheet页内写数据件,会导致excel文件损坏。如果有测试成功的,欢迎提供相关代码。

后续优化

  • 报表模块与业务模块分离,导出报表不会影响核心模块功能,同时提升报表模块性能。
  • 数据库采用一主多从的方式,报表模块读取从库数据,提升性能,虽然从库和主库的数据存在延迟,但是查询报表的数据基本都是隔天数据,所以影响不大。

猜你喜欢

转载自juejin.im/post/7083110449197105160