大家好,我是 V 哥。使用EasyExcel進行大數據量導出時容易導致內存溢出,特別是在導出百萬級別的數據時。你有遇到過這種情況嗎,以下是V 哥整理的解決該問題的一些常見方法,分享給大家,歡迎一起討論:
EasyExcel大數據量導出常見方法
1. 分批寫入
- EasyExcel支持分批寫入數據,可以將數據分批加載到內存中,分批寫入Excel文件,避免一次性將大量數據加載到內存中。
- 示例代碼:
String fileName = "large_data.xlsx";
ExcelWriter excelWriter = EasyExcel.write(fileName).build();
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
// 假設每次寫入10000條數據
int batchSize = 10000;
List<Data> dataList;
int pageIndex = 0;
do {
// 分頁獲取數據
dataList = getDataByPage(pageIndex++, batchSize);
excelWriter.write(dataList, writeSheet);
} while (dataList.size() == batchSize);
// 關閉資源
excelWriter.finish();
2. 設置合適的JVM內存
-
針對大數據導出場景,可以嘗試增大JVM的內存分配,例如:
java -Xms512M -Xmx4G -jar yourApp.jar -
解釋:
-Xms512M:設置初始堆大小為512MB。-Xmx4G:設置最大堆大小為4GB。
3. 減少數據對象的複雜性
- 導出數據時,儘量簡化數據對象,避免不必要的嵌套和多餘字段的加載,以減少對象佔用的內存空間。
4. 關閉自動列寬設置
- EasyExcel的自動列寬功能會佔用大量內存,特別是在數據量較大的情況下。關閉自動列寬可以節省內存。
-
示例代碼:
EasyExcel.write(fileName) .registerWriteHandler(new SimpleWriteHandler()) // 不使用自動列寬 .sheet("Sheet1") .doWrite(dataList);
5. 使用Stream導出(適合大數據)
- 利用
OutputStream分批寫入數據,減少內存消耗。通過BufferedOutputStream可以進一步提高性能。 -
示例代碼:
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(fileName))) { ExcelWriter excelWriter = EasyExcel.write(out).build(); WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build(); int pageIndex = 0; List<Data> dataList; do { dataList = getDataByPage(pageIndex++, batchSize); excelWriter.write(dataList, writeSheet); } while (dataList.size() == batchSize); excelWriter.finish(); } catch (IOException e) { e.printStackTrace(); }
6. 選擇合適的數據導出工具
- 如果數據量非常大,可以考慮切換到支持更高性能的導出工具(如Apache POI的
SXSSFWorkbook),適合導出百萬級別數據量,但配置和使用會更復雜。
亮點來了,那要如何使用 POI 的 SXSSFWorkbook來導出百萬級別的數據量呢?
Apache POI的SXSSFWorkbook 實現百萬級別數據量的導出案例
使用Apache POI的SXSSFWorkbook可以處理大數據量的Excel導出,因為SXSSFWorkbook基於流式寫入,不會將所有數據加載到內存中,而是使用臨時文件進行緩存,這樣可以顯著減少內存消耗,適合百萬級別數據的導出。下面我們來看一個完整的實現示例。
代碼如下
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class LargeDataExportExample {
public static void main(String[] args) {
// 文件輸出路徑
String filePath = "vg_large_data_export.xlsx";
// 導出百萬級數據
exportLargeData(filePath);
}
private static void exportLargeData(String filePath) {
// 每次寫入的批次大小
final int batchSize = 10000;
// 數據總條數
final int totalRows = 1_000_000;
// 創建SXSSFWorkbook對象,內存中只保留100行,超過的部分會寫入臨時文件
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
workbook.setCompressTempFiles(true); // 啓用臨時文件壓縮
// 創建工作表
Sheet sheet = workbook.createSheet("Large Data");
// 創建標題行
Row headerRow = sheet.createRow(0);
String[] headers = {"ID", "Name", "Age"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
int rowNum = 1; // 數據開始的行號
try {
// 按批次寫入數據
for (int i = 0; i < totalRows / batchSize; i++) {
// 模擬獲取每批數據
List<Data> dataList = getDataBatch(rowNum, batchSize);
// 將數據寫入到Excel中
for (Data data : dataList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(data.getId());
row.createCell(1).setCellValue(data.getName());
row.createCell(2).setCellValue(data.getAge());
}
// 處理完成一批數據後,可以選擇清除緩存數據,防止內存溢出
((SXSSFSheet) sheet).flushRows(batchSize); // 清除已寫的行緩存
}
// 將數據寫入文件
try (FileOutputStream fos = new FileOutputStream(filePath)) {
workbook.write(fos);
}
System.out.println("數據導出完成:" + filePath);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 關閉workbook並刪除臨時文件
workbook.dispose();
}
}
/**
* 模擬分頁獲取數據
*/
private static List<Data> getDataBatch(int startId, int batchSize) {
List<Data> dataList = new ArrayList<>(batchSize);
for (int i = 0; i < batchSize; i++) {
dataList.add(new Data(startId + i, "Name" + (startId + i), 20 + (startId + i) % 50));
}
return dataList;
}
// 數據類
static class Data {
private final int id;
private final String name;
private final int age;
public Data(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
}
來解釋一下代碼
- SXSSFWorkbook:
SXSSFWorkbook(100)表示內存中最多保留100行數據,超過的部分會寫入臨時文件,節省內存。 - 批次處理:通過
batchSize控制每批次寫入的數據量,以減少內存消耗。totalRows設置為1,000,000表示導出100萬條數據。 - 模擬數據生成:
getDataBatch方法模擬分頁獲取數據,每次返回一批數據。 - 清除緩存行:每次寫入一批數據後,通過
flushRows(batchSize)將緩存的行從內存中清除,以控制內存佔用。 - 壓縮臨時文件:
workbook.setCompressTempFiles(true)啓用臨時文件壓縮,進一步減少磁盤空間佔用。
需要注意的事項
- 臨時文件:SXSSFWorkbook會在系統臨時文件夾中生成臨時文件,需要確保磁盤空間足夠。
- 資源釋放:完成數據寫入後需要調用
workbook.dispose()以清理臨時文件。 - 性能優化:可根據機器內存調整
batchSize和SXSSFWorkbook緩存行數,避免頻繁刷新和內存溢出。