大家好,我是 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
快取行數,避免頻繁重新整理和記憶體溢位。