专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Java高效Excel导出方案:百万数据秒级导出,后台性能优化技巧

哈喽,大家好,我是BiggerBoy!

之前分享了一篇百万数据秒级导出!Java后台高效Excel导出方案全解析,有朋友们希望能提供一下源码便于学习,最近比较忙,今天它终于来了,抽时间整理了一下发出来。

效果:

环境:本地windows11 + Java8

数据库:MySQL

JVM:堆内存总大小4g,年轻代2g

本地测试结果如下:

控制台输出:导出100万数据19秒

img_1

全表数据量:486万+

img_2

本次导出数据量:100万整

img_3

导出文件大小:50MB

img_4

导出业务流程:

开始 
            分页查询数据 
            根据总量和指定的每个文件数据量计算文件个数 
            根据文件数据量和指定的每个sheet数据量计算sheet个数 
            开启多线程写入文件 

关键代码:

这个方法包含了导出的主流程:

1.获取数据

2.指定导出文件名等相关信息

3.使用Easyexcel导出文件

public void exportOrdersToExcel(int type) throws IOException {
    long timeMillis= System.currentTimeMillis();
    //1.获取数据
    List<TOrders> data = getOrders(type);
    
    //2.指定导出文件名、每个文件数据量,每个文件sheet数
    ExportConfig<TOrders> exportConfig = new ExportConfig<>();
    exportConfig.setData(data);
    exportConfig.setFileDataSize(100000);
    exportConfig.setFileName("t-order-");
    exportConfig.setSheetDataSize(10000);
    exportConfig.setHeaderClass(TOrders.class);
    
    //3.使用easyexcel导出
    ExportService<TOrders> exportService = new ExportService<>();
    exportService.exportToExcel(exportConfig, executor);
    logger.info("导出完成,耗时:{}s", (System.currentTimeMillis() - timeMillis) / 1000);
}

获取数据:

因为数据量比较大,牵涉到深分页的问题,所以下面代码获取数据采用的游标方式,即根据lastId + limit分批查询,比传统分页查询性能好很多。

深分页问题和优化方案可以参考之前的这篇[MySQL 深分页性能优化终极指南:告别慢查询的 5 大方案][MySQL _ 5]

public List<TOrders> getByLastId(Integer lastId, Integer size) {
    List<TOrders> tOrdersAll = new ArrayList<>();
    List<TOrders> tOrders = tOrdersMapper.selectByLastId(lastId, size);
    while (CollectionUtils.isNotEmpty(tOrders)) {
        tOrdersAll.addAll(tOrders);
        lastId = tOrders.get(tOrders.size() - 1).getOrderId();
        if (lastId == null || lastId == 0) {
            break;
        }
        tOrders = tOrdersMapper.selectByLastId(lastId, size);
    }
    return tOrdersAll;
}

使用EasyExcel导出:

exportService.exportToExcel的源码:

  • 调用exportConfig.getExportConfig()方法,获取分割后的文件配置

  • 分割后,使用exportConfigList,采用多线程并行写入多个文件

  • 使用线程池执行写入操作

  • 获取sheet数据,使用EasyExcel写入

    • 写入完成后,countDownLatch减1

  • 等待所有线程执行完成

public voidexportToExcel(ExportConfig<T> exportConfig, Executor executor) {
    //1.调用exportConfig.getExportConfig()方法,获取分割后的文件配置
    List<ExportConfig<T>> exportConfigList = exportConfig.getExportConfig();
    
    //2.分割后,使用exportConfigList,采用多线程并行写入多个文件
    CountDownLatchcountDownLatch=newCountDownLatch(exportConfigList.size());
    for (inti=0; i < exportConfigList.size(); i++) {
        ExportConfig<T> config = exportConfigList.get(i);
        intfinalI= i;
        //3.使用线程池执行写入操作
        executor.execute(() -> {
            StringpathName="C:\\export\\" + config.getFileName() + "-" + System.currentTimeMillis() + "-" + finalI + ".xlsx";
            try (ExcelWriterexcelWriter= EasyExcel.write(pathName, config.getHeaderClass()).build()) {
                //4.获取sheet数据,使用EasyExcel写入
                config.getSheetData().forEach((sheetConfig, data) -> {
                    WriteSheetwriteSheet= EasyExcel.writerSheet(sheetConfig.getSheetNo(), sheetConfig.getSheetName()).build();
                    excelWriter.write(data, writeSheet);
                });
            } catch (Exception e) {
                thrownewRuntimeException(e);
            } finally {
                //5.写入完成后,countDownLatch减1
                countDownLatch.countDown();
            }
        });
    }
    try {
        //6.等待所有线程执行完成
        countDownLatch.await();
    } catch (InterruptedException e) {
        thrownewRuntimeException(e);
    }
}

文件分割:exportConfig.getExportConfig()

分割文件,根据数据总数和指定的每个文件数据量进行分割。

public List<ExportConfig<T>> getExportConfig() {
    if (CollectionUtils.isNotEmpty(data) && fileDataSize != null && data.size() > fileDataSize) {
        List<ExportConfig<T>> exportConfigs = Lists.newArrayList();
        //分成多个文件
        List<List<T>> fileDataPartition = Lists.partition(data, fileDataSize);
        for (List<T> fileData : fileDataPartition) {
            ExportConfig<T> exportConfig = newExportConfig<>(this); // 使用拷贝构造函数
            exportConfig.setData(fileData); // 重置数据
            processSheetData(fileData, exportConfig);
            exportConfigs.add(exportConfig);
        }
        return exportConfigs;
    } else {
        processSheetData(data, this);
        return Lists.newArrayList(this);
    }
}

最后启动项目,调用导出,查看导出结果:

img_5

未经允许不得转载:搜云库 » Java高效Excel导出方案:百万数据秒级导出,后台性能优化技巧

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们