当前位置: 首页 > news >正文

EasyExcel处理大数据量导出

1、简述

        在使用 EasyExcel 处理大数据量导出时,主要需要考虑内存占用和性能问题。EasyExcel 相比传统 POI 库的优势在于其采用流式处理,避免将全部数据加载到内存中,非常适合大数据量导出场景。

2、处理方法

        处理Excel大数据量导出时,传统方式容易导致内存溢出(OOM)。EasyExcel通过一些设计很好地解决了这个问题。下面是一个核心策略和注意事项的表格总结,帮助你快速了解:

特性维度传统POI (HSSF/XSSF)EasyExcel (优化方案)
核心机制对象模型 (DOM) - 全量加载到内存流式写入 & 事件驱动 (SAX)
内存管理每个Cell约1KB,百万数据GB级内存占用,易OOM分批次处理(默认100行),对象复用磁盘缓存(临时文件),内存占用低(~50MB下)
性能表现数据量超5万行后性能可能急剧下降平稳处理百万级数据
API与易用性直接操作对象树,相对直观但大数据时繁琐基于注解或简单API,封装复杂逻辑,使用简便
样式支持支持复杂样式支持基本样式,复杂样式需自定义WriteHandler
  • 流式写入(Streaming Write)
    • 通过 Apache POI 的 SXSSFWorkbook 实现,将数据分批次写入磁盘,而非一次性加载到内存。
    • 默认每 100 行数据生成一个临时文件块,避免内存溢出(OOM)。
  • 事件驱动模型:
    • 通过注解驱动(如 @ExcelProperty)自动解析数据模型,生成表头和内容。
    • 使用模板引擎动态填充数据,支持复杂格式和样式。
  • 内存优化:
    • 对象复用:复用 Cell 和 Row 对象,减少 JVM 垃圾回收压力。
    • 分片处理:按需加载数据,适合大数据量场景(如百万级数据导出)。

EasyExcel避免OOM的核心原理

        EasyExcel 之所以能有效避免内存溢出,核心在于其流式处理机制和内存优化设计,主要体现在以下几点:

  • 按需加载数据:不一次性将所有数据加载到内存,而是分批次读取和写入,每次仅处理部分数据。
  • 基于 SXSSF 的滑动窗口:底层采用 POI 的 SXSSFWorkbook,通过设置滑动窗口大小(如setRandomAccessWindowSize(n)),只在内存中保留最近的 n 行数据,超过的自动写入临时磁盘文件,大幅减少内存占用。
  • 即时释放资源:写入一批数据后可立即清理列表,配合自动刷新机制,避免数据在内存中堆积。
  • 无模型映射优化:支持不定义实体类的写法,减少对象创建带来的内存开销。

        这些设计让 EasyExcel 能在处理几十万甚至上百万行数据时,始终将内存占用控制在较低水平,从而有效避免传统 POI 因加载全部数据导致的内存溢出问题。

SXSSFWorkbook参数配置

ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), LargeData.class).registerWriteHandler(new WorkbookWriteHandler() {@Overridepublic void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder) {Workbook workbook = writeWorkbookHolder.getWorkbook();if (workbook instanceof SXSSFWorkbook) {((SXSSFWorkbook) workbook).setCompressTempFiles(true); // 启用临时文件压缩}WorkbookWriteHandler.super.afterWorkbookCreate(writeWorkbookHolder);}}).build();

        EasyExcel 底层在处理大数据时,会使用SXSSFWorkbook(POI 的流式处理类),它会将超出指定行数的数据写入临时文件,而不是全部保存在内存中。通过设置setRandomAccessWindowSize(n),可以控制内存中最多保留 n 行数据,超过的行会被写入磁盘临时文件,从而有效控制内存占用。不过EasyExcel 本身不直接提供修改滑动窗口大小的 API

3、示例代码

package com.ybw.write;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.ybw.entity.LargeData;
import com.ybw.service.BigDataService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;import java.util.List;/*** @author ybw* @version V1.0* @className WriteExcel* @date 2025/8/25**/
@Component
@Slf4j
public class WriteExcel {@Resourceprivate BigDataService bigDataService;public void writeLargeData() {String fileName = "large_data.xlsx";// 初始化ExcelWritertry (ExcelWriter excelWriter = EasyExcel.write(fileName, LargeData.class).build()) {//1、创建WriteSheetWriteSheet writeSheet = EasyExcel.writerSheet("测试数据").build();//2、初始化数据,从第一页开始,每页获取5000条int pageSize = 5000;int pageIndex = 1;//3、分页处理数据doHandleExcel(pageIndex, pageSize, excelWriter, writeSheet);//4、非常重要:关闭资源,此时才会真正写入磁盘并合并临时文件excelWriter.finish();}}/*** 分页处理数据(递归)** @param pageIndex   页码* @param pageSize    每页大小* @param excelWriter ExcelWriter* @param writeSheet  WriteSheet* @methodName: doHandleExcel* @return: void* @author: ybw* @date: 2025/8/25**/private void doHandleExcel(int pageIndex, int pageSize, ExcelWriter excelWriter, WriteSheet writeSheet) {//1、分页查询数据库:使用你的MyBatis或JPA方法,这里是示例List<LargeData> dataList = bigDataService.getDataByPage(pageIndex, pageSize);if (CollectionUtils.isEmpty(dataList)) {// 没有数据,结束循环return;}//2、写入当前批次数据excelWriter.write(dataList, writeSheet);//3、手动清理列表,释放内存dataList.clear();//4、继续处理下一页次数据doHandleExcel(++pageIndex, pageSize, excelWriter, writeSheet);}
}
  • 分页查询数据库:避免大数据造成java大对象,造成内存溢出。
  • excelWriter.write(dataList, writeSheet):数据并不会立即直接写入最终的Excel文件磁盘位置,而是会先写入到临时文件中。这个过程是 EasyExcel 能够高效处理大数据量且避免内存溢出(OOM)的关键设计。
  • excelWriter.finish():关闭资源,此时才会真正写入磁盘并合并临时文件。
特性维度excelWriter.write(dataList, writeSheet)excelWriter.finish()
核心作用将数据分批写入临时文件合并所有临时文件,生成最终Excel文件,并清理临时文件
数据去向内存(对象复用池) → 磁盘临时文件 (默认每100行一个临时文件块)临时文件 → 最终的目标Excel文件
内存管理采用流式写入对象复用机制,显著降低内存占用释放整个写入过程中占用的内存资源
是否必须是(多次调用,用于循环写入分页查询的数据)是(且必须调用,否则会导致临时文件残留且最终文件不完整或不可用)
临时文件状态写入过程中产生完成后自动清理

4、结合文件服务器

核心思路是

        将耗时的、消耗资源的大数据量导出过程与主应用服务分离,通过异步方式在后台生成文件,并上传到文件服务器,从而释放主应用服务器的资源(特别是内存和CPU)。

对比传统方案

方案核心流程优点缺点适用场景
同步导出(传统方式)用户请求 → 应用服务器实时处理并导出 → 浏览器下载实现简单,即时反馈极易导致应用服务器OOM,请求超时,阻塞其他用户请求数据量极小(<1万行)的导出
异步导出+文件服务器(推荐)用户触发 → 记录任务状态为“处理中” → 后台异步线程导出 → 上传至文件服务器(如OSS/S3) → 更新状态为“完成”并提供下载链接避免OOM,不阻塞请求,用户体验好,支持超大数据量,下载链接减轻服务器带宽压力架构稍复杂,需要实现任务状态跟踪和文件管理所有大数据量(>1万行)的导出场景

优势

  • 根本解决OOM:应用服务器只负责生成一个临时文件,然后立即上传并删除,内存压力极小。
  • 高可用和可扩展:即使应用服务器重启,异步任务也可以通过记录恢复。文件服务器本身具备高可用性。
  • 提升用户体验:用户无需长时间等待浏览器卡住,可以随时查看任务进度。
  • 卸载带宽压力:用户直接从文件服务器下载大文件,不消耗应用服务器的出口带宽。

参考文章:

为什么EasyExcel能处理大数据量而不内存溢出,EasyExcel原理-EW帮帮网

Java EasyExcel导出报表内存溢出全解析 🚀-腾讯云开发者社区-腾讯云

http://www.dtcms.com/a/357153.html

相关文章:

  • 新手法务合同审查,有什么建议?
  • 单点登录(SSO)前端(Vue2.X)改造
  • 关于锁相放大器(LIA)的系统论文研究(重点于FPGA部分)
  • 设计模式:装饰模式(Decorator Pattern)
  • iOS开发之苹果系统包含的所有字体库
  • 最小生成树——Kruskal
  • 【机器学习入门】3.1 关联分析——从“购物篮”到推荐系统的核心逻辑
  • 响应式编程框架Reactor【2】
  • Windows C盘完全占满会如何?
  • 2024-06-13-debian12安装Mariadb-Galera-Cluster+Nginx+Keepalived高可用多主集群
  • 毕马威 —— 公众对人工智能的信任、态度及使用情况调查
  • C++基础(②VS2022创建项目)
  • docker compose设置命令别名的方法
  • Windows WizTree-v4.27.0.0-x64[磁盘空间分析软件]
  • C++中类,this指针,构造函数,析构函数。拷贝构造函数,初步理解运算符重载,初步理解赋值运算符重载
  • 2.4G串口透传模组 XL2417D无线模块,实测通讯距离300m左右!
  • 第23章笔记|PowerShell 高级远程控制配置(端点、自定义、双向认证、多跳)
  • 常见视频编码格式对比
  • GraphRAG 知识图谱核心升级:集成 langextract 与 Gemini ----实现高精度实体与关系抽取
  • 捡捡java——2、基础05
  • Redis不同场景下的注意事项
  • 如何在FastAPI中玩转全链路追踪,让分布式系统故障无处遁形?
  • 【golang长途旅行第34站】网络编程
  • c++ template
  • Vue2+Element 初学
  • LRU 内存淘汰策略
  • 【51单片机定时1秒中断控制流水灯方向】2022-11-14
  • Geocodify 的 API
  • 以技术赋能强化消费者信任,助推餐饮服务质量提质增效的明厨亮灶开源了
  • 有鹿机器人:用智能清洁重塑多行业工作方式