前言

在我们的日常工作中,经常会有Excel数据导出的需求。

但可能会遇到性能和内存的问题。

今天这篇文章跟大家一起聊聊Excel高性能导出的方案,希望对你会有所帮助。

1 传统方案的问题

很多小伙伴门在开发数据导出功能时,习惯性使用Apache POI的HSSF/XSSF组件。

这类方案在数据量超过5万行时,会出现明显的性能断崖式下跌。

根本原因在于内存对象模型的设计缺陷:每个Cell对象占用约1KB内存,百万级数据直接导致JVM堆内存爆炸。

示例代码(反面教材):

// 典型内存杀手写法
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
for (int i = 0; i < 1000000; i++) {
Row row = sheet.createRow(i); // 每行产生Row对象
row.createCell(0).setCellValue("数据"+i); // 每个Cell独立存储
}

这种写法会产生约100万个Row对象和1000万个Cell对象(假设每行10列),直接导致内存占用突破1GB。

更致命的是频繁Full GC会导致系统卡顿甚至OOM崩溃。

2 流式处理架构设计

高性能导出的核心在于内存与磁盘的平衡

这里给出两种经过生产验证的方案:

方案一:SXSSFWorkbook

使用SXSSFWorkbook类,它是Apache POI的增强版。

具体示例如下:

// 内存中只保留1000行窗口
SXSSFWorkbook workbook = new SXSSFWorkbook(1000);
Sheet sheet = workbook.createSheet();
for (int i = 0; i < 1000000; i++) {
Row row = sheet.createRow(i);
// 写入后立即刷新到临时文件
if(i % 1000 == 0) {
((SXSSFSheet)sheet).flushRows(1000);
}
}

通过设置滑动窗口机制,将已处理数据写入磁盘临时文件,内存中仅保留当前处理批次。实测百万数据内存占用稳定在200MB以内。

方案二:EasyExcel

EasyExcel是阿里巴巴开源的Excel高性能处理框架,目前在业界使用比较多。

最近EasyExcel的作者又推出了FastExcel,它是EasyExcel的升级版。

// 极简流式API示例
String fileName = "data.xlsx";
EasyExcel.write(fileName, DataModel.class)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.sheet("Sheet1")
.doWrite(data -> {
// 分页查询数据
int page = 0;
while (true) {
List<DataModel> list = queryByPage(page, 5000);
if (CollectionUtils.isEmpty(list)) break;
data.write(list);
page++;
}
});

该方案通过事件驱动模型对象复用池技术,百万数据导出内存占用可控制在50MB以下。

其核心优势在于:

  1. 自动分批加载数据(默认每批次5000条)
  2. 通过反射缓存消除重复对象创建
  3. 内置样式优化策略避免内存碎片

3 数据库查询的黄金法则

即便导出工具优化到位,若数据查询环节存在瓶颈,整体性能仍会大打折扣。这里给出三个关键优化点:

3.1 解决深度分页问题

传统分页查询在百万级数据时会出现性能雪崩:

SELECT * FROM table LIMIT 900000, 1000 -- 越往后越慢!

正确姿势应使用游标方式:

// 基于自增ID的递进查询
Long lastId = 0L;
int pageSize = 5000;
do {
List<Data> list = jdbcTemplate.query(
"SELECT * FROM table WHERE id > ? ORDER BY id LIMIT ?",
new BeanPropertyRowMapper<>(Data.class),
lastId, pageSize);
if(list.isEmpty()) break;
lastId = list.get(list.size()-1).getId();
// 处理数据...
} while (true);

该方案利用索引的有序性,将时间复杂度从O(N²)降为O(N)。

3.2 减少字段数量

-- 错误写法:全字段查询
SELECT * FROM big_table -- 正确姿势:仅取必要字段
SELECT id,name,create_time FROM big_table

实测显示,当单行数据从20个字段缩减到5个字段时,查询耗时降低40%,网络传输量减少70%。

3.3 连接池参数调优

# SpringBoot配置示例
spring:
datasource:
hikari:
maximum-pool-size: 20 # 根据CPU核数调整
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000

导出场景建议使用独立连接池,避免影响主业务。

连接数计算公式:线程数 = CPU核心数 * 2 + 磁盘数

4 生产级进阶技巧

4.1 异步分片导出

想要提升Excel数据导出的性能,我们必须使用多线程异步导出的方案。

具体示例如下:

@Async("exportExecutor")
public CompletableFuture<String> asyncExport(ExportParam param) {
// 1. 计算分片数量
int total = dataService.count(param);
int shardSize = total / 100000; // 2. 并行处理分片
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < shardSize; i++) {
int finalI = i;
futures.add(CompletableFuture.runAsync(() -> {
exportShard(param, finalI * 100000, 100000);
}, forkJoinPool.commonPool()));
} // 3. 合并文件
CompletableFuture.allOf(futures.toArray(new CompletableFuture)
.thenApply(v -> mergeFiles(shardSize));
return CompletableFuture.completedFuture(taskId);
}

通过分治策略将任务拆解为多个子任务并行执行,结合线程池管理实现资源可控。

4.2 配置JVM参数

我们需要配置JVM参数,并且需要对这些参数进行调优:

// JVM启动参数示例
-Xmx4g -Xms4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:ParallelGCThreads=4
-XX:ConcGCThreads=2
-XX:InitiatingHeapOccupancyPercent=35

这样可以有效的提升性能。

导出场景需特别注意:

  • 年轻代与老年代比例建议2:1
  • 避免创建超过50KB的大对象
  • 使用对象池复用DTO实例

4.3 整体方案

Excel高性能导出的方案如下图所示:

用户点击导出按钮,会写入DB,生成一个唯一的任务ID,任务状态为待执行。

然后后台异步处理,可以分页将数据写入到Excel中(这个过程可以使用多线程实现)。

将Excel文件存储到云存储中。

然后更新任务状态为以完成。

最后通过WebSocket通知用户导出结果。

5 总结

经过多个千万级项目的锤炼,我们总结出Excel高性能导出的黄金公式:

高性能 = 流式处理引擎 + 分页查询优化 + 资源管控

具体实施时可参考以下决策树:

最后给小伙伴们的三个忠告:

  1. 切忌过早优化:在需求明确前不要盲目选择复杂方案
  2. 监控先行:务必埋点记录导出耗时、内存波动等关键指标
  3. 兜底策略:始终提供CSV导出选项作为保底方案

希望本文能帮助大家在数据导出的战场上,真正实现"百万数据,弹指之间"!

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的50万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

Excel百万数据高性能导出方案!的更多相关文章

  1. SQL SERVER 与ACCESS、EXCEL的数据导入导出转换

    * 说明:复制表(只复制结构,源表名:a 新表名:b)      select * into b from a where 1<>1 * 说明:拷贝表(拷贝数据,源表名:a 目标表名:b) ...

  2. 利用PHPExcel读取Excel的数据和导出数据到Excel

    PHPExcel是一个PHP类库,用来帮助我们简单.高效实现从Excel读取Excel的数据和导出数据到Excel.也是我们日常开发中,经常会遇到的使用场景.比如有个客户信息表,要批量导出发给同事,我 ...

  3. SQL SERVER 和ACCESS、EXCEL的数据导入导出

    SQL SERVER 与ACCESS.EXCEL之间的数据转换SQL SERVER 和ACCESS的数据导入导出[日期:2007-05-06]     来源:Linux公社  作者:Linux 熟 悉 ...

  4. PHP百万级数据导出方案(多csv文件压缩)

    本文转自网络仅供学习之用 概述: 最近公司项目要求把数据除了页面输出也希望有导出功能,虽然之前也做过几个导出功能,但这次数据量相对比较大,差不多一天数据就20W条,要求导7天或者30天,那么数据量就轻 ...

  5. 六、ibatis1.2.8查询性能优化,实现百万数据zip导出

    经测试发现将查询的结果100万数据(池子中共有大概14亿的数据)写入Excle文件并进行压缩导出zip文件最耗时的地方竟然在查询,因此本篇文章主要是针对如何在spring+ibatis1.2.8中优化 ...

  6. sql语句百万数据量优化方案

    一:理解sql执行顺序 在sql中,第一个被执行的是from语句,每一个步骤都会产生一个虚拟表,该表供下一个步骤查询时调用,比如语句:select top 10 column1,colum2,max( ...

  7. <搬运> SQL语句百万数据量优化方案

    一:理解sql执行顺序 在sql中,第一个被执行的是from语句,每一个步骤都会产生一个虚拟表,该表供下一个步骤查询时调用,比如语句:select top 10 column1,colum2,max( ...

  8. excel表格数据导入导出

    /** * 导出数据到excel表格 * Created by shenjianhua on 2018-12-28 */ package com.luer.comm.excel; import jav ...

  9. PHP 百万级数据导出方案(多 CSV 文件压缩)

    ps:来源 :https://laravel-china.org/articles/15944/php-million-level-data-export-scheme-multi-csv-file- ...

  10. phpexcel如何读取excel的数据和如何导出数据到excel

    phpexcel如何读取excel的数据和如何导出数据到excel 一.总结 一句话总结:去官网看参考手册和api,或者找中文的博客或者参考手册 1.phpexcel插件如何下载? 其实这些插件不仅可 ...

随机推荐

  1. Luogu P9055 [集训队互测 2021] 数列重排 题解 [ 紫 ] [ 构造 ] [ 数学 ]

    数列重排:差点就场切的神仙构造,最后一步想假了,导致我模拟赛荣获 25+5+0 的好成绩! 这题部分分很有启发性,跟着一步一步打基本能想到正解的构造,但也有可能想偏部分分的意思,想假策略. 构造 先看 ...

  2. Johnson 全源负权最短路径算法详解

    Floyd-Warshall算法可以求解出图内任意两点的最短路径,适用于稠密图,但时间复杂度为 \(O(n³)\):Dijkstra算法求解单源最短路径的时间复杂度为 \(O(m + n log n) ...

  3. 开源一款I2C电机驱动扩展板-FreakStudio多米诺系列

    总线直流电机扩展板 原文链接: FreakStudio的博客 摘要 设计了一个I2C电机驱动板,通过I2C接口控制多个电机的转速和方向,支持刹车和减速功能.可连接16个扩展板,具有PWM输出.过流过热 ...

  4. autMan奥特曼机器人-代理池配置教程

    一.优势: 全可视化 稳如老牛(从2.8.6开始) 隧道代理和接口获取,使用灵活 代理池运行状态指令可查:代理池 二.启用代理池并设置服务端口 代理池的启用与关闭,均为重启autMan生效 设置隧道代 ...

  5. Java - JVM及其调优

    原文链接:https://blog.csdn.net/qq_27098537/article/details/124436788 一.什么是JVM 用于运行java代码,包括一套字节码指令集.一组寄存 ...

  6. Archlinux 更新失败之驱动与 Xorg 配置错误

    Archlinux系统更新是滚动更新,所以更新失败又被叫做"滚挂了" 此次滚挂发生在1月27日,过了那么久了才想起来该记录了-- 现象 滚挂的现象是,能够进系统,但是笔记本电脑自带 ...

  7. PPT_标题

    一 调节字体大小 1.字体-字魂71号-御守锦书 2.更改字体大小(138.96.80.80.96.138) 3.字体背景 复制背景图片->选择ppt文字->设置图片格式->选择来自 ...

  8. CCRC软件开发评审-材料应该怎么准备

    1. 什么是CCRC软件开发评审 软件安全开发资质认证是对软件开发方的基本资格.管理能力.技术能力和软件安全过程能力等方面进行评价全软件开发服务资质级别是衡量服务提供方的软件安全开发服务资格和能力的尺 ...

  9. js回忆录(3) -- 循环语句,前后缀运算符

    计算机对于大批量数据的处理速度比起人类不知道快了多少,因此对于重复的操作,使用循环语句处理是很方便的,对于我们前端来说,给同一标签的元素绑定事件啦,tab切换啦,左右联动效果啦,等等都可以使用循环语句 ...

  10. go kratos protobuf 接收动态JSON数据

    前言 google.protobuf.Struct 是 Google Protocol Buffers 中的一种特殊类型,用于表示动态的键值对数据.它可以存储任意类型的数据,并提供了方便的方法来访问和 ...