EasyExcel大数据量导出优化及Apache POI的SXSSFWorkbook实践

2024-12-03 11:38 更新

Welcome to the Java原创学习手册 wiki!大家好,我是 V 哥。使用EasyExcel进行大数据量导出时容易导致内存溢出,特别是在导出百万级别的数据时。你有遇到过这种情况吗,以下是V 哥整理的解决该问题的一些常见方法,分享给大家,欢迎一起讨论:

EasyExcel大数据量导出常见方法

1. 分批写入

  • EasyExcel支持分批写入数据,可以将数据分批加载到内存中,分批写入Excel文件,避免一次性将大量数据加载到内存中。
  • 示例代码
    1. String fileName = "large_data.xlsx";
    2. ExcelWriter excelWriter = EasyExcel.write(fileName).build();
    3. 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();

  1. #### 2. 设置合适的JVM内存
  2. - 针对大数据导出场景,可以尝试增大JVM的内存分配,例如:
  3. ```shell
  4. java -Xms512M -Xmx4G -jar yourApp.jar
  • 解释
    • -Xms512M:设置初始堆大小为512MB。
    • -Xmx4G:设置最大堆大小为4GB。

3. 减少数据对象的复杂性

  • 导出数据时,尽量简化数据对象,避免不必要的嵌套和多余字段的加载,以减少对象占用的内存空间。

4. 关闭自动列宽设置

  • EasyExcel的自动列宽功能会占用大量内存,特别是在数据量较大的情况下。关闭自动列宽可以节省内存。
  • 示例代码
    1. EasyExcel.write(fileName)
    2. .registerWriteHandler(new SimpleWriteHandler()) // 不使用自动列宽
    3. .sheet("Sheet1")
    4. .doWrite(dataList);

5. 使用Stream导出(适合大数据)

  • 利用OutputStream分批写入数据,减少内存消耗。通过BufferedOutputStream可以进一步提高性能。
  • 示例代码
    1. try (OutputStream out = new BufferedOutputStream(new FileOutputStream(fileName))) {
    2. ExcelWriter excelWriter = EasyExcel.write(out).build();
    3. WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
    4. int pageIndex = 0;
    5. List<Data> dataList;
    6. do {
    7. dataList = getDataByPage(pageIndex++, batchSize);
    8. excelWriter.write(dataList, writeSheet);
    9. } while (dataList.size() == batchSize);
    10. excelWriter.finish();
    11. } catch (IOException e) {
    12. e.printStackTrace();
    13. }

6. 选择合适的数据导出工具

  • 如果数据量非常大,可以考虑切换到支持更高性能的导出工具(如Apache POI的SXSSFWorkbook),适合导出百万级别数据量,但配置和使用会更复杂。

亮点来了,那要如何使用 POI 的 SXSSFWorkbook来导出百万级别的数据量呢?

Apache POI的SXSSFWorkbook 实现百万级别数据量的导出案例

使用Apache POI的SXSSFWorkbook可以处理大数据量的Excel导出,因为SXSSFWorkbook基于流式写入,不会将所有数据加载到内存中,而是使用临时文件进行缓存,这样可以显著减少内存消耗,适合百万级别数据的导出。下面我们来看一个完整的实现示例。

代码如下

  1. import org.apache.poi.ss.usermodel.*;
  2. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. public class LargeDataExportExample {
  8. public static void main(String[] args) {
  9. // 文件输出路径
  10. String filePath = "vg_large_data_export.xlsx";
  11. // 导出百万级数据
  12. exportLargeData(filePath);
  13. }
  14. private static void exportLargeData(String filePath) {
  15. // 每次写入的批次大小
  16. final int batchSize = 10000;
  17. // 数据总条数
  18. final int totalRows = 1_000_000;
  19. // 创建SXSSFWorkbook对象,内存中只保留100行,超过的部分会写入临时文件
  20. SXSSFWorkbook workbook = new SXSSFWorkbook(100);
  21. workbook.setCompressTempFiles(true); // 启用临时文件压缩
  22. // 创建工作表
  23. Sheet sheet = workbook.createSheet("Large Data");
  24. // 创建标题行
  25. Row headerRow = sheet.createRow(0);
  26. String[] headers = {"ID", "Name", "Age"};
  27. for (int i = 0; i < headers.length; i++) {
  28. Cell cell = headerRow.createCell(i);
  29. cell.setCellValue(headers[i]);
  30. }
  31. int rowNum = 1; // 数据开始的行号
  32. try {
  33. // 按批次写入数据
  34. for (int i = 0; i < totalRows / batchSize; i++) {
  35. // 模拟获取每批数据
  36. List<Data> dataList = getDataBatch(rowNum, batchSize);
  37. // 将数据写入到Excel中
  38. for (Data data : dataList) {
  39. Row row = sheet.createRow(rowNum++);
  40. row.createCell(0).setCellValue(data.getId());
  41. row.createCell(1).setCellValue(data.getName());
  42. row.createCell(2).setCellValue(data.getAge());
  43. }
  44. // 处理完成一批数据后,可以选择清除缓存数据,防止内存溢出
  45. ((SXSSFSheet) sheet).flushRows(batchSize); // 清除已写的行缓存
  46. }
  47. // 将数据写入文件
  48. try (FileOutputStream fos = new FileOutputStream(filePath)) {
  49. workbook.write(fos);
  50. }
  51. System.out.println("数据导出完成:" + filePath);
  52. } catch (IOException e) {
  53. e.printStackTrace();
  54. } finally {
  55. // 关闭workbook并删除临时文件
  56. workbook.dispose();
  57. }
  58. }
  59. /**
  60. * 模拟分页获取数据
  61. */
  62. private static List<Data> getDataBatch(int startId, int batchSize) {
  63. List<Data> dataList = new ArrayList<>(batchSize);
  64. for (int i = 0; i < batchSize; i++) {
  65. dataList.add(new Data(startId + i, "Name" + (startId + i), 20 + (startId + i) % 50));
  66. }
  67. return dataList;
  68. }
  69. // 数据类
  70. static class Data {
  71. private final int id;
  72. private final String name;
  73. private final int age;
  74. public Data(int id, String name, int age) {
  75. this.id = id;
  76. this.name = name;
  77. this.age = age;
  78. }
  79. public int getId() {
  80. return id;
  81. }
  82. public String getName() {
  83. return name;
  84. }
  85. public int getAge() {
  86. return age;
  87. }
  88. }
  89. }

来解释一下代码

  1. SXSSFWorkbookSXSSFWorkbook(100)表示内存中最多保留100行数据,超过的部分会写入临时文件,节省内存。
  2. 批次处理:通过batchSize控制每批次写入的数据量,以减少内存消耗。totalRows设置为1,000,000表示导出100万条数据。
  3. 模拟数据生成getDataBatch方法模拟分页获取数据,每次返回一批数据。
  4. 清除缓存行:每次写入一批数据后,通过flushRows(batchSize)将缓存的行从内存中清除,以控制内存占用。
  5. 压缩临时文件workbook.setCompressTempFiles(true)启用临时文件压缩,进一步减少磁盘空间占用。

需要注意的事项

  • 临时文件:SXSSFWorkbook会在系统临时文件夹中生成临时文件,需要确保磁盘空间足够。
  • 资源释放:完成数据写入后需要调用workbook.dispose()以清理临时文件。
  • 性能优化:可根据机器内存调整batchSizeSXSSFWorkbook缓存行数,避免频繁刷新和内存溢出。EasyExcel大数据量导出优化及Apache POI的SXSSFWorkbook实践
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号