百万级 Excel 数据导出最佳实践
本文分享了百万级 Excel 数据导出的优化方案,包括 分批查询 + 分页写入 以降低内存占用,以及 异步导出 + 个人中心下载 以提升用户体验,避免 HTTP 超时问题。通过优化方案,可高效、安全地处理大数据导出,提高系统稳定性。
·
前言
在实际工作中,我们经常会遇到 月底运营或客户要求导出大量业务数据 的情况。如果数据量在 几万行 以内,普通导出方式尚能应对。但当数据量增长到 几十万甚至上百万行 时,传统方法往往会 性能下降、内存溢出,甚至导致服务器崩溃。那么,该如何高效地处理 百万级 Excel 数据导出 呢?本文将分享优化方案,帮助你轻松应对大数据导出挑战。
思路
分治思想:分批查询 + 分页写入
一次性查询 100 万条数据,数据库和服务器的内存往往会承受不了,导致 查询超时、系统崩溃。因此,我们可以采用 分批查询 + 分页写入 的方式,避免内存占用过高。
思路如下:
分页查询数据:将 100 万条数据 分成多个批次,每次查询 1 万条,减少数据库压力。
循环写入 Excel:查询到 1 万条数据后,立即写入 Excel,避免一次性加载所有数据到内存。
封装导出方法:将 分页查询 + 写入逻辑 封装成通用方法,自动处理大数据导出。
代码实现
这里使用EasyExcel执行导出操作,DEFAULT_PAGE_SIZE 可根据实际需要修改对应值。
package com.ji.jichat.excel.util.service;
import cn.hutool.core.date.StopWatch;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.github.pagehelper.ISelect;
import com.ji.jichat.common.pojo.PageDTO;
import com.ji.jichat.common.pojo.PageVO;
import com.ji.jichat.mybatis.util.JiPageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class ExcelExportService<T> {
/**
* 每次查询的默认分页大小
**/
private static final int DEFAULT_PAGE_SIZE = 5000;
/**
* 导出excel
*
* @param fileName 导出文件名
* @param query 导出的查询方法
* @param dataModelClass 导出返回实体类
* @author jisl on 2025/2/6 14:58
**/
public void exportToExcel(String fileName, ISelect query, Class<T> dataModelClass) {
// 获取总记录数
final StopWatch stopWatch = new StopWatch("导出开始:" + fileName);
stopWatch.start("总数查询");
final long totalRecords = JiPageHelper.doSelectPageInfo(new PageDTO(1, 10), query).getTotal();
final long totalPages = (totalRecords + DEFAULT_PAGE_SIZE - 1) / DEFAULT_PAGE_SIZE;
log.info("导出totalRecords=%s,totalPages=%s", totalRecords, totalPages);
stopWatch.stop();
stopWatch.start("分页导出");
// 创建 ExcelWriter 对象
final ExcelWriter excelWriter = EasyExcel.write(fileName).build();
// 分页查询并写入数据
for (int currentPage = 1; currentPage <= totalPages; currentPage++) {
// 分页查询数据
final PageVO<T> pageVO = JiPageHelper.doSelectPageInfo(new PageDTO(currentPage, DEFAULT_PAGE_SIZE), query);
// 写入数据到当前页的Sheet
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet " + currentPage).head(dataModelClass).build();
excelWriter.write(pageVO.getList(), writeSheet);
}
// 完成写入
excelWriter.finish();
stopWatch.stop();
log.info("导出用时:" + stopWatch.prettyPrint(TimeUnit.MILLISECONDS));
}
}
导出调用方法
@Test
public void exportDataToExcel() {
ExcelExportService excelExportService=new ExcelExportService();
final SystemSmsCodePageDTO dto = SystemSmsCodePageDTO.builder().build();
excelExportService.exportToExcel("C:\\Newand\\file\\导出测试.xlsx", () -> systemSmsCodeService.page(dto), SystemSmsCodeExcelVO.class);
}
实际效果
导出用时:StopWatch '导出开始:C:\Newand\file\导出测试.xlsx': running time = 4528 ms
---------------------------------------------
ms % Task name
---------------------------------------------
000000477 11% 总数查询
000004051 89% 分页导出
源码
下载
在处理百万级数据量的 Excel 导出时,直接通过 HTTP 让用户等待文件生成是不可取的,因为:
响应超时:HTTP 请求有超时时间,长时间等待会导致请求失败。
用户体验差:用户需要长时间等待,无法进行其他操作。
优化方案:异步导出 + 个人中心下载
更好的解决方案是:
用户提交导出请求 → 后台异步处理,立即返回任务提交成功的响应。
后台任务生成 Excel → 任务执行完成后,将文件存储至服务器或云存储。
用户个人中心查看进度 → 处理完成后,用户可以在个人中心查看并下载文件。
总结
以上是笔者在大数据导出方面的一些实践经验分享,希望对大家有所帮助。

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)