springboot應用大批量導出excel產生oom處理措施實踐(適用于poieasyexcel)

一、背景:

在某些信息管理場景中,存在大批量導出需求,例如一次性導出10~100w行excel數據,如果不做特殊的處理,很容易導致Out Of Memory,特別是堆內存溢出。

oom復現

例如修改IDEA運行配置,VM參數初始堆為256m,最大堆為1G,垃圾回收器為G1

-Xms256m -Xmx1G -XX:+UseG1GC

在這里插入圖片描述

Apply之后重啟應用,重啟之后打開Java VisualVM,連接剛啟動的進程,關注監控tab頁面的內存使用情況
在這里插入圖片描述
使用poi導出,單批次為10w
在這里插入圖片描述
等待一段時間,觸發了oom異常:
在這里插入圖片描述

在這里插入圖片描述

此時其它業務操作也會受到影響,例如此處分頁查詢已經失效,只能重啟臨時解決。
在這里插入圖片描述

二、分批處理實踐

思路:每次查詢并寫入指定數量的excel行,文本設置為1w。

service代碼:

package com.tgh.securitydemo.service;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tgh.securitydemo.entity.CommonWorkOrderLog;
import com.tgh.securitydemo.entity.excel.WorkOrderLogExportVO;
import com.tgh.securitydemo.entity.excel.WorkOrderLogSearchVO;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;/*** @author PineTree* @description: 導出服務* @date 2025/4/27 17:51*/
@Service
public class ExportService {@Autowiredprivate ICommonWorkOrderLogService commonWorkOrderLogService;private static final int batchSize = 10000; // 每批次大小public void exportExcel(WorkOrderLogSearchVO searchVO, HttpServletResponse response) throws IOException {// 設置響應頭response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setHeader("Content-Disposition", "attachment; filename=work_order_logs.xlsx");if (searchVO.isEasy()) {if (searchVO.isBatch()) {handleEasyExcelBatch(searchVO, response);} else {handleEasyExcel(searchVO, response);}} else {if (searchVO.isBatch()) {handlePoiBatch(searchVO, response);} else {handlePoiExcel(searchVO, response);}}}private void handlePoiBatch(WorkOrderLogSearchVO searchVO, HttpServletResponse response) throws IOException {// 創建SXSSFWorkbook,設置行訪問窗口大小為100SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 保留3500行在內存中Sheet sheet = workbook.createSheet("日志數據-poi分批");boolean hasNext = true;try {int currentPage = 1;int rowNum = 0;Row row = sheet.createRow(rowNum++);generatePoiHead(row);while (hasNext) {// 分頁查詢數據IPage<CommonWorkOrderLog> pageSearch = new Page<>(currentPage, batchSize);IPage<CommonWorkOrderLog> pageData = commonWorkOrderLogService.getOrderPage(pageSearch, new CommonWorkOrderLog());List<CommonWorkOrderLog> records = pageData.getRecords();if (records.isEmpty()) {break; // 沒有數據了,退出循環}for (CommonWorkOrderLog record : records) {Row poiRow = sheet.createRow(rowNum++);generatePoiDateRow(record, poiRow);}// 刷新已寫入的行((SXSSFSheet)sheet).flushRows(batchSize);// 判斷是否還有下一頁hasNext = pageData.getCurrent() * batchSize < searchVO.getExportCount();currentPage++;}workbook.write(response.getOutputStream());} finally {workbook.dispose();workbook.close();}}private void handleEasyExcelBatch(WorkOrderLogSearchVO searchVO, HttpServletResponse response) throws IOException {ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), WorkOrderLogExportVO.class).build();try {WriteSheet writeSheet = EasyExcel.writerSheet("日志數據-easy分批").build();int currentPage = 1;boolean hasNext = true;while (hasNext) {// 分頁查詢數據IPage<CommonWorkOrderLog> pageSearch = new Page<>(currentPage, batchSize);IPage<CommonWorkOrderLog> pageData = commonWorkOrderLogService.getOrderPage(pageSearch, new CommonWorkOrderLog());List<CommonWorkOrderLog> records = pageData.getRecords();List<WorkOrderLogExportVO> exportData = records.stream().map(WorkOrderLogExportVO::fromEntity).collect(Collectors.toList());// 寫入當前批次數據excelWriter.write(exportData, writeSheet);// 判斷是否還有下一頁hasNext = pageData.getCurrent() * batchSize < searchVO.getExportCount();currentPage++;}} finally {// 關閉ExcelWriterif (excelWriter != null) {excelWriter.finish();}}}private void handleEasyExcel(WorkOrderLogSearchVO searchVO, HttpServletResponse response) throws IOException {IPage<CommonWorkOrderLog> page = new Page<>(1, searchVO.getExportCount());IPage<CommonWorkOrderLog> orderPage = commonWorkOrderLogService.getOrderPage(page, new CommonWorkOrderLog());List<CommonWorkOrderLog> records = orderPage.getRecords();List<WorkOrderLogExportVO> exportData = records.stream().map(WorkOrderLogExportVO::fromEntity).collect(Collectors.toList());// 使用EasyExcel導出EasyExcel.write(response.getOutputStream(), WorkOrderLogExportVO.class).sheet("日志數據-easy單批").doWrite(exportData);}private void handlePoiExcel(WorkOrderLogSearchVO searchVO, HttpServletResponse response) throws IOException {IPage<CommonWorkOrderLog> page = new Page<>(1, searchVO.getExportCount());IPage<CommonWorkOrderLog> orderPage = commonWorkOrderLogService.getOrderPage(page, new CommonWorkOrderLog());List<CommonWorkOrderLog> records = orderPage.getRecords();// 創建工作簿Workbook workbook = new XSSFWorkbook();Sheet sheet = workbook.createSheet("日志數據-poi單批");// 創建表頭Row headerRow = sheet.createRow(0);generatePoiHead(headerRow);// 填充數據int rowNum = 1;for (CommonWorkOrderLog record : records) {Row poiRow = sheet.createRow(rowNum++);generatePoiDateRow(record, poiRow);}// 寫入響應流workbook.write(response.getOutputStream());workbook.close();}private static void generatePoiHead(Row headerRow) {headerRow.createCell(0).setCellValue("工單日志ID");headerRow.createCell(1).setCellValue("操作類型");headerRow.createCell(2).setCellValue("工單ID");headerRow.createCell(3).setCellValue("工單名稱");headerRow.createCell(4).setCellValue("工單類型");headerRow.createCell(5).setCellValue("業務位置ID");headerRow.createCell(6).setCellValue("計劃完成時間");headerRow.createCell(7).setCellValue("實際完成時間");headerRow.createCell(8).setCellValue("處理人賬號");headerRow.createCell(9).setCellValue("處理人名稱");headerRow.createCell(10).setCellValue("創建時間");headerRow.createCell(11).setCellValue("創建人名稱");headerRow.createCell(12).setCellValue("最后更新人名稱");headerRow.createCell(13).setCellValue("業務屬性1");headerRow.createCell(14).setCellValue("業務屬性2");}private static void generatePoiDateRow(CommonWorkOrderLog record, Row poiRow) {poiRow.createCell(0).setCellValue(record.getWorkOrderLogId());poiRow.createCell(1).setCellValue(record.getOperationType());poiRow.createCell(2).setCellValue(record.getWorkOrderId());poiRow.createCell(3).setCellValue(record.getWorkOrderName());poiRow.createCell(4).setCellValue(record.getWorkOrderType());poiRow.createCell(5).setCellValue(record.getBizLocationId());poiRow.createCell(6).setCellValue(record.getPlannedCompletionTime().toString());poiRow.createCell(7).setCellValue(ObjectUtils.isNotEmpty(record.getActualCompletionTime()) ? record.getActualCompletionTime().toString() : "");poiRow.createCell(8).setCellValue(record.getHandleUserAccount());poiRow.createCell(9).setCellValue(record.getHandleUserName());poiRow.createCell(10).setCellValue(record.getCreateTime().toString());poiRow.createCell(11).setCellValue(record.getCreateByName());poiRow.createCell(12).setCellValue(record.getLastUpdateName());poiRow.createCell(13).setCellValue(record.getBizAttribute1());poiRow.createCell(14).setCellValue(record.getBizAttribute2());}}

三、測試驗證

poi分批導出10w

在這里插入圖片描述

poi分批導出100w

在這里插入圖片描述

easyexcel分批導出10w

在這里插入圖片描述

easyexcel分批導出100w

在這里插入圖片描述

四、結論

poieasyexcel大批量導出,均可通過分批思路來處理oom異常,easyexcel導出比poi慢,但是生成的文件小于poi,本文僅僅解決了大批量excel導出從不能導到能導。如果同時存在多用戶,高頻率,大批量導出同樣會出問題,后續討論…。

代碼免費倉獲取完整代碼:
前端:https://gitee.com/pinetree-cpu/hello_vue3
后端:https://gitee.com/pinetree-cpu/parent-demon

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79041.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79041.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79041.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

谷歌在即將舉行的I/O大會之前,意外泄露了其全新設計語言“Material 3 Expressive”的細節

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

深入理解負載均衡:傳輸層與應用層的原理與實戰

目錄 前言1. 傳輸層&#xff08;Layer 4&#xff09;負載均衡1.1 工作層級與核心機制1.2 實現方式詳解1.3 優缺點分析1.4 典型實現工具 2. 應用層&#xff08;Layer 7&#xff09;負載均衡2.1 工作層級與核心機制2.2 實現方式解析2.3 優缺點分析2.4 常用實現工具 3. Layer 4 與…

PyTorch 版本、torchvision 版本和 Python 版本的對應關系

PyTorch 版本、torchvision 版本和 Python 版本的對應關系 在深度學習領域&#xff0c;PyTorch 及其配套庫 torchvision 的使用極為廣泛。但不同版本的 PyTorch、torchvision 與 Python 之間存在嚴格的對應關系&#xff0c;若版本搭配不當&#xff0c;會導致代碼運行出錯…

【hadoop】Hbase java api 案例

代碼實現&#xff1a; HBaseConnection.java package com.peizheng.bigdata;import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client…

榮耀A8互動娛樂組件部署實錄(第3部分:控制端結構與房間通信協議)

作者&#xff1a;曾在 WebSocket 超時里泡了七天七夜的苦命人 一、控制端總體架構概述 榮耀A8控制端主要承擔的是“運營支點”功能&#xff0c;也就是開發與運營之間的橋梁。它既不直接參與玩家行為&#xff0c;又控制著玩家的行為邏輯和游戲規則觸發機制。控制端的主要職責包…

Vue3路由模式為history,使用nginx部署上線后刷新404的問題

一、問題 在使用nginx部署vue3的項目后&#xff0c;發現正常時可以訪問的&#xff0c;但是一旦刷新&#xff0c;就是出現404的情況 二、解決方法 1.vite.config.js配置 在vite.config.js中加入以下配置 export default defineConfig(({ mode }) > {const isProduction …

企業級UI測試的“雙保險”:TestComplete的智能對象識別與詳細報告功能

企業級UI測試真是讓人頭疼&#xff01;界面元素變來變去&#xff0c;測試腳本動不動就報錯&#xff0c;測試工作根本沒法順利推進。而且&#xff0c;測試結果的管理和共享也麻煩得很&#xff0c;團隊協作效率大打折扣。別急&#xff01;TestComplete的智能對象識別和詳細測試報…

SpringBoot的自動配置和起步依賴原理

關于Spring Boot的自動配置和起步依賴&#xff0c;我想結合最新的實現機制來展開說明。先說自動配置——這是Spring Boot最核心的"約定優于配置"思想的落地體現。舉個例子&#xff0c;當我們創建一個新的Spring Boot項目時&#xff0c;只要在pom.xml里添加了spring-b…

《MATLAB實戰訓練營:從入門到工業級應用》高階挑戰篇-《5G通信速成:MATLAB毫米波信道建模仿真指南》

《MATLAB實戰訓練營&#xff1a;從入門到工業級應用》高階挑戰篇-5G通信速成&#xff1a;MATLAB毫米波信道建模仿真指南 &#x1f680;&#x1f4e1; 大家好&#xff01;今天我將帶大家進入5G通信的奇妙世界&#xff0c;我們一起探索5G通信中最激動人心的部分之一——毫米波信…

一、Redis快速入門

Redis的常見命令和客戶端使用 一、初識Redis Redis是一種鍵值型的NoSql數據庫&#xff0c;這里有兩個關鍵字&#xff1a; 鍵值型 NoSql 其中鍵值型&#xff0c;是指Redis中存儲的數據都是以key、value對的形式存儲&#xff0c;而value的形式多種多樣&#xff0c;可以是字符…

gitcode 上傳文件報錯文件太大has exceeded the limited size (10 MiB) in commit

登陸gitcoe&#xff0c;在項目設置->提交設置 ,勾選提交文件限制&#xff0c;修改限制的大小。 修改完后&#xff0c;重新提交代碼。

【運維】構建基于Python的自動化運維平臺:用Flask和Celery打造高效管理工具

《Python OpenCV從菜鳥到高手》帶你進入圖像處理與計算機視覺的大門! 解鎖Python編程的無限可能:《奇妙的Python》帶你漫游代碼世界 隨著企業IT基礎設施的復雜性不斷增加,手動運維已無法滿足高效管理的需求。本文詳細介紹如何基于Python構建一個自動化運維平臺,利用Flask…

基于大模型預測的產鉗助產分娩全方位研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與方法 二、產鉗助產分娩概述 2.1 產鉗助產定義與歷史 2.2 適用情況與臨床意義 三、大模型預測原理與數據基礎 3.1 大模型技術原理 3.2 數據收集與處理 3.3 模型訓練與驗證 四、術前預測與準備 4.1 大模型術前風險預…

css使用aspect-ratio制作4:3和9:16和1:1等等比例布局

文章目錄 1. 前言2. 用法2.1 基本語法2.2. 與max-width、max-height等屬性結合使用2.3. 動態計算比例 3. 應用場景4. 兼容性和替代方案5. 總結 1. 前言 在網頁制作過程中&#xff0c;有時候我們只知道寬度&#xff0c;或者只知道高度&#xff0c;這時候需要制作一個4:3和9:16這…

【國產化】在銀河麒麟ARM環境下離線安裝docker

1、前言 采用離線安裝的方式。 關于離線安裝的方式官網有介紹&#xff0c;但是說的很簡單&#xff0c;網址&#xff1a;Binaries | Docker Docs 官網介紹的有幾種主流linux系統的安裝方式&#xff0c;但是沒有kylin的&#xff0c;所以在此記錄一下。 在安裝過程中也遇到了些…

從一城一云到AI CITY,智慧城市進入新階段

AI將如何改變城市面貌&#xff1f;AI能否為城市創造新的商業價值&#xff1f;AI的落地應用將對日常生活有什么樣的影響&#xff1f; 幾乎在每一場和城市發展相關的論壇上&#xff0c;都會出現以上幾個問題。城市既是AI技術創新融合應用的綜合性載體&#xff0c;普羅大眾對AI產…

鴻蒙知識總結

判斷題 1、 在http模塊中&#xff0c;多個請求可以使用同一個httpRequest對象&#xff0c;httpRequest對象可以復用。&#xff08;錯誤&#xff09; 2、訂閱dataReceiverProgress響應事件是用來接收HTTP流式響應數據。&#xff08;錯誤&#xff09; 3、ArkTS中變量聲明時不需要…

[人機交互]理解用戶

一.解釋什么是認知&#xff0c;以及它對交互設計的重要性 1.1什么是認知 認知是指與knowing相關的能力&#xff0c;行為和過程&#xff08;考填空&#xff09; -如何感知物理刺激&#xff1f;如注意、知覺等 -如何認識自我、他人以及環境&#xff1f;如意識、記憶等 -如何…

微信小程序備案的一些記錄

小程序如果沒有備案是搜索不到小程序的。 小程序備案需要填寫主體負責人的信息&#xff0c;需要主體負責人的手機號驗證碼&#xff0c; 需要填寫管理員的信息&#xff0c;同樣也需要驗證手機號碼&#xff0c; 填寫完畢之后&#xff0c;提交進行初審&#xff0c;初審之后會打…

SpringCloud服務拆分:Nacos服務注冊中心 + LoadBalancer服務負載均衡使用

SpringCloud中Nacos服務注冊中心 LoadBalancer服務負載均衡使用 前言Nacos工作流程nacos安裝docker安裝window安裝 運行nacos微服務集成nacos高級特性1.服務集群配置方法效果圖模擬服務實例宕機 2.權重配置3.環境隔離 如何啟動集群節點本地啟動多個節點方法 LoadBalancer集成L…