簡體   English   中英

使用 apache-poi 在 Java 中寫入較大的 Excel 導致 CPU 使用率過高

[英]Writing large Excel in Java causing high CPU usage using apache-poi

使用 25 列寫入大約 1/2 百萬條記錄的大數據。

使用 apache-poi 流工作簿將列表中的數據寫入 excel 文件。 在本地測試時,它也會在本地機器中產生高 CPU 峰值。 似乎是在將工作簿數據寫入文件時引起的

workbook.write(fileOutputStream) // it is causing CPU spikes debugged and confirmed.

它導致雲應用程序(部署在 kube.netes 中)的 CPU 使用率很高,並在達到資源限制時重新啟動應用程序。 我們有一個帶有 2042Mi memory 和 1024m CPU 配置的簡單應用程序。

有什么方法可以在不影響 CPU 和 Memory 和 java 堆的情況下有效地寫入一個大的 excel 文件。

(注意:不能使用 csv 或其他格式,因為業務要求是針對 excel 文件)

代碼使用:

import java.io.File;
import java.io.FileOutputStream;
import java.util.List;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.stereotype.Service;

import com.king.medicalcollege.model.Medico;

@Service
public class ExcelWriterService {

    // file is an empty file already created
    // Large List around 500K records of medico data [Medico is POJO]

    public File writeData(File file, List<Medico> medicos) {

        SXSSFWorkbook sxssfWorkbook = null;
        try (SXSSFWorkbook workbook = sxssfWorkbook = new SXSSFWorkbook(1);
                FileOutputStream fileOutputStream = new FileOutputStream(file)) {

            Sheet sheet = workbook.createSheet();
            CellStyle cellStyle = workbook.createCellStyle();
            int rowNum = 0;
            for (Medico medico : medicos) {
                Row row = sheet.createRow(rowNum);
                //just adding POJO values (25 fields)  into ROW 
                addDataInRow(medico, row, cellStyle);
                rowNum++;
            }

            //workbook.write causing CPU spike
            workbook.write(fileOutputStream);

            workbook.dispose();

        } catch (Exception exception) {
            return null;
        } finally {
            if (sxssfWorkbook != null) {
                sxssfWorkbook.dispose();
            }
        }

        return file;
    }

    private void addDataInRow(Medico medico, Row row, CellStyle cellStyle) {
        Cell cell_0 = row.createCell(0);
        cell_0.setCellValue(medico.getFirstName());
        cell_0.setCellStyle(cellStyle);
        
        Cell cell_1 = row.createCell(1);
        cell_1.setCellValue(medico.getMiddleName());
        cell_1.setCellStyle(cellStyle);
        
        Cell cell_2 = row.createCell(2);
        cell_2.setCellValue(medico.getLastName());
        cell_2.setCellStyle(cellStyle);
        
        Cell cell_3 = row.createCell(2);
        cell_3.setCellValue(medico.getFirstName());
        cell_3.setCellStyle(cellStyle);
        
        //...... around 25 columns will be added like this
    }
}

問題“有沒有辦法在不影響 CPU 和 Memory 的情況下寫入一個大的 excel 文件?” 就像是:讓我吃我的蛋糕,也把它吃掉。 在德語中我們說:洗我,但不要弄濕我。 換句話說:計算機上一個文件中的所有內容必須經過CPU處理,必須在memory中處理。

要了解我們正在談論的 memory 的數量,讓我們簡單計算一下 500,000 行和 25 列意味着什么:

                     Cell value                                                                         Len     500,000 times  KiByte        MiByte
Single value         some cell value                                                                    15          
25 columns           some cell value, some cell value, some cell value, some cell value, some ...       425     212500000      207519.5313   202.6557922
XML of single value  <c r="C99" t="inlineStr" s="9"><is><t>some cell value</t></is></c>                 66       
XML of 25 columns    <c r="C99" t="inlineStr" s="9"><is><t>some cell value</t></is></c><c r="...        1650    825000000      805664.0625   786.781311

這表明,即使只有純文本,500,000 行和 25 列的單元格值“某個單元格值”也將占用 memory 的 202.6557922 MiByte。

但是 Excel 文件不僅僅是純文本。 當前的 Open Office Excel 格式存儲 XML。由於 XML 開銷,這需要更多的 memory。 上面顯示,當存儲為 XML 時,具有單元格值“某些單元格值”的 500,000 行和 25 列將占用 memory 的 786.781311 MiByte。

memory 的 786.781311 MiByte 僅用於存儲單元格,存儲行、工作表、工作簿、styles、關系、...

SXSSFWorkbook聲稱是一種流式處理方法。 但它只是將單元格作為臨時文件流式傳輸到工作表中。 它還需要 memory 來保存工作簿,styles,關系,...流式處理后,需要 memory 將它們全部放入工作簿。 至少在這個過程中,整個工作簿的大小必須通過 CPU 和 memory 來處理。

結論:Excel是一個電子表格應用。 它不是一種適合數據交換的格式。 適合數據交換的格式是:純文本 (CSV) 或純文本 XML 或 JSON,因為它們確實可以包含純數據行流而無需工作簿中的工作表開銷。

如果你的業務需求堅持在Excel個文件,那就給應用更多的資源。

我在Get smart Excel table with Apache POI works using organizations-1000000.csv 1000000.csv 中提供的代碼是從https://www.datablist.com/learn/csv/download-sample-csv-files下載的。 它創建了一個Excel.xlsx ,其中包含 1,000,000 個數據行和 97,450 KB 的大小。

對我來說java -XX:+PrintFlagsFinal -version | findstr HeapSize java -XX:+PrintFlagsFinal -version | findstr HeapSize打印:

   size_t ErgoHeapSizeLimit                        = 0                                         {product} {default}
   size_t HeapSizePerGCThread                      = 43620760                                  {product} {default}
   size_t InitialHeapSize                          = 132120576                                 {product} {ergonomic}
   size_t LargePageHeapSizeThreshold               = 134217728                                 {product} {default}
   size_t MaxHeapSize                              = 2113929216                                {product} {ergonomic}
   size_t MinHeapSize                              = 8388608                                   {product} {ergonomic}
    uintx NonNMethodCodeHeapSize                   = 5832780                                {pd product} {ergonomic}
    uintx NonProfiledCodeHeapSize                  = 122912730                              {pd product} {ergonomic}
    uintx ProfiledCodeHeapSize                     = 122912730                              {pd product} {ergonomic}
   size_t SoftMaxHeapSize                          = 2113929216                             {manageable} {ergonomic}
java version "15" 2020-09-15
Java(TM) SE Runtime Environment (build 15+36-1562)
Java HotSpot(TM) 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)

您似乎通過給 SXSSFWorkbook 一個 window 大小來做正確的事情(盡管1可能太小導致問題?)。 當行數超過您設置的限制時,工作簿應該被刷新到磁盤,從而減少 memory 的使用。 我懷疑是否有減少 CPU 使用率的解決方法。

您可以嘗試通過調整 JVM 參數來限制您的 memory 使用,這樣它就不會觸發 K8s 限制。 看看這些: -Xmx -Xms -XX:MaxRAM -XX:+UseSerialGC

您是否考慮過使用備用庫來編寫 Excel 文件? 例如,看看這個 SO 答案: Are there any alternatives to using Apache POI Java for Microsoft Office?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM