1. 程式人生 > 其它 >百萬級Excel匯出方案

百萬級Excel匯出方案

技術標籤:系統架構匯出百萬excel

百萬資料量excel匯出方案

  • 編寫目的

在實際場景中excel報表匯出業務比較常見,隨著業務量及資料量遞增,報表匯出資料量大的情況下容易出現記憶體溢位,響應耗時太長等問題,現提供百萬資料量內的報表匯出方案,供大家參考。

  • 適用範圍

Java開發工程師

  • 方案說明
  • 問題說明
  1. 大批量資料匯出容易瞬間打滿老年代導致Full GC頻繁發生,容易系統卡死。
  2. 一次性把目標資料全部查詢出來再寫到流中的方式,大量被查詢的物件駐留在堆記憶體中,直接打滿整個堆,容易堆記憶體溢位。

  • 核心思路
  1. 底層使用POI Excel SXSSF,Excel 2007一個頁籤只能放1048576行資料、16384列。這裡用alibba開源EsayExcel。
  2. 資料批量分頁查出,輸出到流。如果有自增主鍵,可採用滾動分頁方式,查詢效率優化比較明顯。

SELECT * FROM tableX WHERE id > #{lastBatchMaxId} [其他條件] ORDER BY id [ASC|DESC](這裡一般選用ASC排序) LIMIT ${size}

  • 參考程式碼

Pom引入easyexcel

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.1.4</version>
</dependency>

參考程式碼@

@GetMapping(path = "/exportId")
public void exportId(@RequestParam("pageSize") int pageSize, @RequestParam("pageNum") int pageNum, HttpServletResponse response) throws Exception {

    long startTime = System.currentTimeMillis();
    ExcelWriter writer = null;
    try {
        String fileName = URLEncoder.encode(String.format("%s-(%s).csv", "訂單支付資料", UUID.randomUUID().toString()),
                StandardCharsets.UTF_8.toString());
        response.setContentType("application/force-download");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        writer = new ExcelWriterBuilder()
                .autoCloseStream(true)
                .excelType(ExcelTypeEnum.XLSX)
                .file(response.getOutputStream())
                .head(OrderRecordExportDO.class)
                //註冊自定義時間格式轉換,easyExceL目前支援Date,不支援LocalDateTime,需要自定義時間轉換器
                .registerConverter(new LocalDateTimeConverter())
                .build();
        // xlsx檔案上上限是104W行左右,這裡如果超過104W需要分Sheet
        WriteSheet writeSheet = new WriteSheet();
        writeSheet.setSheetName("target");
        long lastBatchMaxId = 0L;

        //2、查詢原資料表交易
        for (int i = 1; i <= pageNum; i++) {
            long startSelect=System.currentTimeMillis();
            log.debug("i=[{}]", i);

            List<OrderRecordExportDO> orders = orderRecordMapper.exportLimitId(lastBatchMaxId,pageSize);
            log.info("[源資料交易數][{}][{}],耗時={}", i, orders.size(),System.currentTimeMillis()-startSelect);
            lastBatchMaxId = orders.stream().map(OrderRecordExportDO::getId).max(Long::compareTo).orElse(Long.MAX_VALUE);

G測試情況

總數=800000,pageSize=10000,pageNum=80,耗時=48046ms10列 130M

表格樣式,模板檔案匯出可以檢視esayExcel官網。

https://www.yuque.com/easyexcel/doc/easyexcel

參考地址

https://www.cnblogs.com/throwable/p/13285518.html