番摊机器人 关于百万级 Excel 数据导入导出技术的完整梳理。
百万数据 Excel 如何快速导入导出
2026 年 9 月 21 日
核心结论
不能用 POI 的 UserModel 模式(HSSFWorkbook / XSSFWorkbook)。 百万数据直接 OOM。[citation:44.4][citation:44.13]
核心方案:流式处理——读的时候逐行解析不缓存,写的时候逐行写入不保留全量。在这条路上,主流的 Java 方案是 EasyExcel 和 POI SXSSF。[citation:44.2][citation:44.5]
---
一、技术选型对比
导入(读)
| 方案 | 内存占用 | 速度 | 推荐度 |
|:----:|:--------:|:----:|:------:|
| EasyExcel | 极低(SAX 封装,逐行读取) | 快 | ⭐⭐⭐⭐⭐ |
| POI SAX (XSSFReader) | 极低(事件驱动) | 快 | ⭐⭐⭐⭐ |
| POI UserModel | 极高(全量加载) | 慢 | ❌ OOM |
| 原生 CSV 解析 | 极低 | 最快 | ⭐⭐⭐⭐(适合非 xlsx) |
导出(写)
| 方案 | 内存占用 | 速度 | 推荐度 |
|:----:|:--------:|:----:|:------:|
| EasyExcel | 可控(流式写入) | 快 | ⭐⭐⭐⭐⭐ |
| POI SXSSFWorkbook | 可控(磁盘换内存) | 较快 | ⭐⭐⭐⭐ |
| POI XSSFWorkbook | 极高 | 慢 | ❌ OOM |
| CSV 导出 | 极低 | 最快 | ⭐⭐⭐⭐ |
---
二、导入实战:百万行 Excel 入库
2.1 核心原理
EasyExcel 基于 SAX(Simple API for XML)模式——逐行读取 XML 内容,解析一行处理一行,不把整个文件加载到内存。[citation:44.4][citation:44.7]
Excel 文件 → SAX 解析器 → 逐行触发事件 → 回调处理 → 批量写数据库
内存只保持一行
2.2 代码:EasyExcel 逐行读取
// 自定义监听器,逐行处理
public class DemoDataListener implements ReadListener<DemoData> {
private static final int BATCH_SIZE = 500;
private List<DemoData> buffer = new ArrayList<>(BATCH_SIZE);
@Override
public void invoke(DemoData data, AnalysisContext context) {
buffer.add(data);
if (buffer.size() >= BATCH_SIZE) {
saveBatch(); // 每500条批写入一次数据库
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if (!buffer.isEmpty()) {
saveBatch(); // 处理剩余数据
}
}
private void saveBatch() {
// JDBC batch insert / MyBatis-Plus saveBatch
demoMapper.insertBatch(buffer);
buffer.clear();
}
}
// 调用——流式读取,不一次性加载全部
EasyExcel.read(file, DemoData.class, new DemoDataListener()).sheet().doRead();
2.3 数据库写入优化
从"逐行插入"到"批量提交",性能差距很大:[citation:44.10]
逐行插入 → 20 分钟(100 万行)
JDBC batch(500 条一批) → 3 分钟
JDBC batch + 事务控制 + 关闭自动提交 → 95 秒
多线程分片写入(多个 sheet 并发) → 40 秒
关键点:[citation:44.7][citation:44.8]
rewriteBatchedStatements=true(MySQL JDBC 必开)
每批 500-2000 条,太大反而慢(事务日志压力)
commit 频率控制在每批一次
---
三、导出实战:从数据库到 Excel
3.1 核心原理
导出最大的坑是先把所有数据查出来装进 List,再一次性写入 Excel——100 万条 × 1KB/条 = 1GB,加上对象开销和 Excel 缓存,内存直接撑爆。[citation:44.13]
正确做法:流式查询 + 流式写入。
数据库游标 → 逐批读取(每批 1000 条) → EasyExcel 逐行写入 → 生成文件
内存只保留当前批
3.2 代码:流式导出
public void exportLargeExcel(HttpServletResponse response) {
// 流式写入,不缓存全量
EasyExcel.write(response.getOutputStream(), ExportData.class)
.inMemory(false) // 关键:不使用内存模式
.sheet("数据")
.doWrite(() -> {
// 分批查询,每次查 1000 条
int page = 1;
List<ExportData> batch;
while (!(batch = queryBatch(page++, 1000)).isEmpty()) {
return batch; // 分页返回
}
});
}
更可控的做法是手动分批:
try (ExcelWriter excelWriter = EasyExcel.write(fileName, ExportData.class).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet("数据").build();
long lastId = 0;
boolean hasMore = true;
while (hasMore) {
// 游标分页(比 OFFSET 更稳定)
List<ExportData> page = mapper.scrollQuery(lastId, 1000);
if (page.isEmpty()) {
hasMore = false;
} else {
excelWriter.write(page, writeSheet);
lastId = page.get(page.size() - 1).getId();
}
}
}
3.3 Excel 行数限制怎么办
Excel 单个 Sheet 最多 1,048,576 行。超额有两种处理方式:[citation:44.12][citation:44.16]
| 方案 | 做法 | 适用场景 |
|:----:|------|---------|
| 多 Sheet | 每 100 万行切一个 Sheet | 必须用 .xlsx 格式的场景 |
| CSV 降级 | 超过 100 万行自动转 CSV | 海量导出,用户能接受 CSV |
| 压缩包 | 多个 CSV 打包成 ZIP | 配合定时任务导出 |
3.4 SXSSFWorkbook 备选方案
如果项目已经重度依赖 POI,不想引入 EasyExcel,POI 3.8+ 的 SXSSFWorkbook 是官方提供的流式写出版本:[citation:44.16]
SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 内存中只保留 100 行
// 超过 100 行的数据自动刷入磁盘临时文件
// 内存占用稳定在几 MB
// 写入完成后清理临时文件
workbook.dispose();
SXSSF 与 EasyExcel 的对比:
| 对比项 | EasyExcel | POI SXSSF |
|:-----:|:---------:|:---------:|
| 内存控制 | 流式写入,极低 | 窗口式写入,可控 |
| API 简洁度 | 注解驱动,简单 | 原生 POI 风格,较复杂 |
| 样式支持 | 有但有限 | 完全支持 POI 样式 |
| 社区活跃度 | 阿里开源,长期维护 | Apache 官方,稳定 |
---
四、完整架构
生产环境下,百万数据导入导出一个完整的异步架构通常是这样的:[citation:44.2]
用户请求 → 异步任务表(记录状态)
↓
线程池执行 → 流式查询数据库 → 流式写入 Excel
↓
完成后 → 上传 OSS/MinIO → 发送通知 → 用户下载
导入方向反过来:
用户上传 → 流式解析 → 校验 → 批量写入数据库
↓
记录失败行 → 生成错误报告
几个关键设计决策:[citation:44.16]
异步执行:百万数据导出不可能在 HTTP 请求内同步完成,必须后台异步
进度反馈:通过 Redis/数据库记录当前处理的行数,前端轮询显示进度
文件管理:生成后上传 OSS/MinIO,临时文件设置过期自动清理
异常处理:导入时的数据校验错误要逐行记录,最终汇总成错误报告
---
五、不同技术栈的对应方案
| 语言 | 推荐库 | 说明 |
|:----:|:------:|------|
| Java | EasyExcel | 阿里开源,SAX 模式,百万级标准方案 |
| Java | POI SXSSF | 官方方案,适合已有 POI 依赖的项目 |
| Python | openpyxl(只读模式)+ pandas | read_only=True 避免 OOM;CSV 用 chunksize |
| .NET | MiniExcel | 针对大数据量优化的轻量库 |
| Node.js | exceljs(流式模式) | 设置 stream 选项,逐行写入 |
| PHP | PhpSpreadsheet + FastExcel | 内存优化模式 |
| 通用 | CSV | 百万行 CSV 解析最快,没有之一 |
---
六、一句话总结
百万数据 Excel 的核心就四个字:流式处理。 读用 SAX 逐行解析(EasyExcel),写用流式逐行写入(EasyExcel / SXSSF),数据库查用游标分页,入库用批量提交。只要做好"不全量加载到内存"这件事,百万行和一千行的处理思路是一样的。