多人同時導出 Excel 導致內存溢出

1、問題根因分析

????????多人同時導出Excel導致內存溢出(OOM)的核心原因是:在短時間內,大量數據被加載到JVM堆內存中,且創建了大量大對象(如Apache POI的Cell、Row、Sheet對象),超過了堆內存的最大限制(-Xmx)。

  1. 同步處理與高并發:導出請求通常是同步的。當多個用戶同時觸發導出時,每個請求都會在服務器端創建一個處理線程,并在該線程中構建一個完整的、包含大量數據的Excel工作簿(Workbook)對象。

  2. Apache POI的內存模型:傳統的Apache POI(如HSSF/XSSF)在構建Excel時,所有單元格、樣式、數據都保存在內存中的Java對象里。一個幾十萬行的Excel文件,其對應的Workbook對象可能輕松占用幾百MB甚至上GB的內存。

  3. 數據一次性加載:為了生成Excel,通常需要從數據庫一次性查詢出所有數據,這個巨大的ResultSet也會占用大量內存。

  4. JVM堆空間不足:如果JVM堆內存設置本身不大,或者并發導出的用戶數足夠多,就很容易將堆內存耗盡,觸發java.lang.OutOfMemoryError: Java heap space

2、解決方案(從低代價到高代價,從臨時到根本)

2.1?應用層優化(代碼改造 - 最根本的解決方案)

這是最推薦的方式,從根源上解決內存問題。

a) 使用流式API (SXSSF)

Apache POI提供了專門用于處理大數據量的流式API:SXSSF。

原理

SXSSF(Streaming Usermodel API)在XSSF的基礎上擴展,它只會將一部分行(例如100行)保留在內存中,生成一行,刷新一行到磁盤臨時文件,從而實現低內存占用。它通過滑動窗口機制來管理內存中的行。

  • 優點:內存占用極低且恒定(僅與rowAccessWindowSize有關),是解決此問題的最佳武器。

  • 缺點:不支持一些高級特性(如公式計算、單元格合并等在刷新后可能受限),會生成臨時文件。

代碼示例
// 設置一個滑動窗口值,表示在內存中保留多少行,超出的行會被寫入磁盤
int rowAccessWindowSize = 100;
SXSSFWorkbook workbook = new SXSSFWorkbook(rowAccessWindowSize);
SXSSFSheet sheet = workbook.createSheet("數據導出");// 寫入表頭
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("姓名");
headerRow.createCell(1).setCellValue("年齡");// 流式分頁查詢數據庫并寫入
int pageSize = 1000;
int pageNum = 1;
List<Data> dataList;
int currentRow = 1;do {// 1. 分頁查詢,避免一次性加載所有數據dataList = dataService.getExportData(pageNum, pageSize);if (dataList.isEmpty()) {break;}// 2. 將本頁數據寫入Excelfor (Data data : dataList) {Row row = sheet.createRow(currentRow++);row.createCell(0).setCellValue(data.getName());row.createCell(1).setCellValue(data.getAge());// ... 其他單元格}// 3. 非常重要!手動清理掉滑動窗口之外的行,釋放內存// ((SXSSFSheet)sheet).flushRows() 也可以,但注意參數((SXSSFSheet) sheet).flushRows(dataList.size()); // 刷新并清理已處理的行pageNum++;
} while (!dataList.isEmpty());// 將workbook寫入HttpServletResponse的輸出流
workbook.write(response.getOutputStream());
workbook.dispose(); // 刪除臨時文件
workbook.close();

b) 分頁查詢數據庫

如上例所示,在數據獲取層,絕對不要一次性SELECT * FROM huge_table,必須使用分頁查詢(如MySQL的LIMIT offset, size)。這大大降低了數據庫和Java應用兩邊的內存壓力。

2.2?架構與流程優化

a) 異步導出

將同步請求改為異步任務。

  • 流程

    • 用戶點擊導出后,服務端立即返回一個任務ID或一個URL:“正在生成,請稍后查看下載鏈接”。
    • 后臺使用一個獨立的、線程池大小可控的任務(如使用@Async、消息隊列、Job調度)來執行真正的導出操作。
    • 導出完成后,將文件上傳到OSS或文件服務器,并將下載鏈接通過通知系統(站內信、郵件等)告知用戶,或者更新任務狀態供用戶查詢。
  • 優點
    • 避免了HTTP請求超時。

    • 可以對后臺任務隊列進行控流,避免同時處理過多導出任務,從而控制內存使用峰值。

    • 用戶體驗更好,不會因為長時間等待而導致瀏覽器卡死。

  • 缺點:系統設計更復雜,不能立即下載。

b) 限流與隊列

如果必須同步導出,可以在應用入口進行限流。

  • 使用RateLimiter(Guava)或Sentinel等工具,限制單位時間內允許的導出請求數量。例如,最多只允許同時處理2個導出請求,后續請求排隊等待或直接返回“系統繁忙,請稍后再試”。

  • 優點:簡單粗暴,有效防止系統被瞬時高并發打垮。

  • 缺點:用戶體驗差(需要等待或失敗)。

2.3?運維與配置優化(臨時緩解措施)

這些不能根治問題,但可以作為一個緩沖或輔助手段。

a) 增加JVM堆內存

通過調整啟動參數?-Xmx4g -Xms4g?來增大最大堆內存。

  • 優點:簡單,快速。

  • 缺點

    • 只是推遲了OOM發生的時間,如果數據量或并發量持續增長,遲早還會溢出。

    • 大內存會帶來更長時間的Full GC(Garbage Collection),導致應用“卡頓”。

b) 優化GC參數

針對大內存和創建大量短命對象(導出任務中的對象基本都是短命對象)的場景,使用G1垃圾收集器可能效果更好。

  • 參數示例:-XX:+UseG1GC -XX:MaxGCPauseMillis=200

c) 文件拆分與壓縮

對于極端大量的數據,可以考慮不再導出單一Excel文件,而是導出多個壓縮包(如每10萬行一個Excel,然后打包成ZIP)。但這更多是業務邏輯的變更。

2.4 總結

  1. 立即止損(線上緊急情況)

    • 短期:如果正在頻繁OOM,可以先增大堆內存?-Xmx?并重啟服務,快速恢復業務。

    • 同時:在網關/應用層緊急添加導出限流策略,防止問題復發。

  2. 根本解決(中期必須完成)

    • 改造代碼:將導出邏輯從使用HSSFWorkbook/XSSFWorkbook遷移到SXSSFWorkbook

    • 優化數據查詢:確保數據獲取是分頁的,而不是一次性加載。

  3. 優化體驗與架構(長期規劃)

    • 改為異步導出,并提供任務查詢界面。這是對用戶和最系統都最友好的方式。

    • 考慮將生成的大文件存儲到OSS等對象存儲中,減輕應用服務器磁盤IO壓力。

技術選型參考

  • 首選SXSSF?+?分頁查詢?+?異步導出

  • 備選:如果數據模型非常簡單,也可以考慮直接生成CSV文件,CSV是純文本格式,內存開銷遠小于Excel。但缺點是無法處理樣式和多個Sheet。

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

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

相關文章

深入 RAG(檢索增強生成)系統架構:如何構建一個能查資料的大語言模型系統

&#x1f407;明明跟你說過&#xff1a;個人主頁 &#x1f3c5;個人專欄&#xff1a;《深度探秘&#xff1a;AI界的007》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目錄 一、前言 1、LLM 的局限&#xff1a;模型知識“封閉” vs 現實知識…

linux tftpboot燒寫地址分析

1&#xff0c;loadaddr 是一個環境變量&#xff0c;用于指定文件&#xff08;如內核鏡像、設備樹等&#xff09;加載到內存的起始地址。setenv loadaddr 0x82000000setenv loadaddr 0x80008000saveenv //.保存配置將 loadaddr 設置為 0x82000000&#xff0c;表示后續文件將加載…

硬件工程師9月實戰項目分享

目錄 簡介 人員情況 實戰項目簡介 功能需求 需求分析 方案設計 電源樹設計 時鐘樹設計 主芯片外圍設計 接口設計 模擬鏈路設計 PCB設計檢查要點 測試方案設計 硬件測試培訓 測試代碼學習 培訓目標 掌握基本的硬件設計流程 掌握以FPGA為核心的硬件設計業務知識 …

力扣刷題——59.螺旋矩陣II

力扣刷題——59.螺旋矩陣II 題目 給你一個正整數 n &#xff0c;生成一個包含 1 到 n2 所有元素&#xff0c;且元素按順時針順序螺旋排列的 n x n 正方形矩陣 matrix 。示例 1&#xff1a;輸入&#xff1a;n 3 輸出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]示例 2&#xff1a; 輸…

win11系統還原點恢復系統

背景 系統換位bug11后&#xff0c;真的是各種以前的操作和設置找不到&#xff0c;太煩了&#xff0c;我是沒想到&#xff0c;連系統恢復還原點都這么難找。然后搜了一圈都是恢復系統之類的&#xff0c;真的崩潰。只好自己記錄了。 ?內容找到設置—>系統–>系統信息系統信…

DHCP 原理與配置(一)

應用場景隨著網絡規模的不斷擴大&#xff0c;網絡復雜度不斷提升&#xff0c;網絡中的終端設備例如主機、手機、 平板等&#xff0c;位置經常變化。終端設備訪問網絡時需要配置IP地址、網關地址、DNS服務器 地址等。采用手工方式為終端配置這些參數非常低效且不夠靈活。 IETF于…

SARibbon的編譯構建及詳細用法

目錄 1.1 源碼構建 1.2 搭建項目 1.3 詳細用法 1.4 不同風格 1.5 完整代碼 引言:SARibbon是一個專門為Qt框架設計的開源Ribbon風格界面控件庫,它模仿了微軟Office和WPS的Ribbon UI風格,適用于需要復雜菜單和工具欄的大型桌面程序。本文從源碼編譯構建到詳細使用,做了一…

CSS【詳解】性能優化

精簡 CSS移除未使用的 CSS&#xff08;“死代碼”&#xff09;&#xff0c;可借助工具如 PurgeCSS、UnCSS 自動檢測并刪除未被頁面使用的樣式。避免重復樣式&#xff0c;通過提取公共樣式&#xff08;如 mixin 或公共類&#xff09;減少代碼冗余。利用預處理器&#xff08;Sass…

Flutter 線程模型詳解:主線程、異步與 Isolate

一、主線程&#xff1a;默認的執行環境 所有代碼默認運行在主線程。下面的例子展示了一個會阻塞主線程的錯誤示范&#xff1a; import package:flutter/material.dart;void main() {runApp(const MyApp()); }class MyApp extends StatelessWidget {const MyApp({super.key});ov…

ChartDB:可視化數據庫設計工具私有化部署

ChartDB:可視化數據庫設計工具私有化部署一、什么是ChartDB ChartDB 是一款基于 Web 的開源數據庫可視化工具&#xff0c;專為簡化數據庫設計與管理流程而開發。以下是其核心特性與功能概述: 1、核心功能 智能查詢可視化?&#xff1a;通過單條 SQL 查詢即可生成數據庫架構圖&a…

單片機-FreeRTOS(ing)

目錄 一、基礎介紹 1.1 調度策略 1.1.1 調度方式 1.1.2 調度器 1.2 任務以及優先級 1.2.1 任務與協程 1.2.2 任務狀態 1.2.3 任務優先級 1.2.4 任務優先級分配方案 1.3 任務間通信 - 信號量 1.3.1 信號量 1.3.2 任務間計數信號量的實現 1.3.3 中斷方式計數信號量的…

為什么調用API總返回404,該如何調試

當調用一個應用程序接口&#xff08;API&#xff09;時&#xff0c;持續地收到“404 未找到”的錯誤&#xff0c;其核心原因在于客戶端發起的“請求”&#xff0c;未能成功地&#xff0c;匹配到服務器上任何一個“真實存在”的、可供訪問的“資源路徑”。這本質上&#xff0c;是…

醫療信息化自主可控轉型的實踐探索 —— 以常德二院為例

目錄 頭雁領航 - 激發醫療新質生產力 核心支撐 - 電科金倉奠定數據底座 生態共建 - 攜手護航醫療信創發展 信創產業發展是國家經濟數字化轉型、提升產業鏈發展的關鍵&#xff0c;是科技自立自強的核心基座&#xff0c;其本質是實現中國信息化產業的自主可控。醫療信創作為關…

Gin傳參和接收參數的方式

Gin查詢參數和接收參數的方式 常用 Gin 綁定方法對比方法用途特點c.Bind()自動識別 Content-Type最通用&#xff0c;根據請求頭自動選擇綁定方式c.ShouldBindJSON()只綁定 JSON強制使用 JSON 格式&#xff0c;類型明確c.ShouldBindXML()只綁定 XML強制使用 XML 格式c.ShouldBin…

MariaDB/MySQL 客戶端工具與服務端配置精要指南

文章目錄一、客戶端與服務端程序二、用戶賬號管理三、MySQL 客戶端命令3.1 命令類型?3.2 使用模式?3.3 常用選項?3.4 提示符定制?四、mysqladmin管理命令??五、服務端配置?5.1 配置文件???5.2 Socket 通信配置??六、最佳實踐總結免費個人運維知識庫&#xff0c;歡迎…

自動化項目日報生成工具測評與選型:如何匹配團隊日報管理需求

引言在項目管理場景中&#xff0c;手動撰寫日報常面臨多重效率瓶頸&#xff1a;任務數據分散在協作群、Excel 表格、項目看板等多個平臺&#xff0c;匯總時需反復核對&#xff1b;不同成員日報格式不統一&#xff0c;管理層整合分析耗時&#xff1b;任務進度與日報信息不同步&a…

基于SpringBoot+Vue的吳韻蘇香文旅小程序(協同過濾算法、Echarts圖形化分析、騰訊地圖API、二維碼識別)

&#x1f388;系統亮點&#xff1a;協同過濾算法、Echarts圖形化分析、騰訊地圖API、二維碼識別&#xff1b;一.系統開發工具與環境搭建1.系統設計開發工具后端使用Java編程語言的Spring boot框架 項目架構&#xff1a;B/S架構 運行環境&#xff1a;win10/win11、jdk17小程序&a…

python numpy.random的基礎教程(附opencv 圖片轉數組、數組轉圖片)

目錄 1.在區間[a,b)隨機生成n個整數。 2.在區間[a,b)隨機生成n個數。 3.在區間[0,1)生成隨機數 4.打亂順序 5.從指定的列表中選擇 NumPy&#xff08;Numerical Python&#xff09;是一個開源的科學計算庫&#xff0c;專門用于高效處理多維數組&#xff08;ndarray&#xf…

Vue2.x核心技術與實戰(二)

目錄 三、Vue2.x:生命周期+工程化開發(組件入門) 3.1 生命周期 3.1.1 生命周期 & 生命周期四個階段 3.1.2 生命周期鉤子 Vue生命周期鉤子案例 - 新聞列表 & 輸入框自動聚焦 3.2 綜合案例:小黑記賬清單 3.3 工程化開發入門 3.3.1 工程化開發 & 腳手架Vue …

【鴻蒙心跡】7×24小時極限求生:當Origin_null遇上鴻蒙,我如何用100杯咖啡換一條跨域活路?

文章概要 大家好&#xff0c;我是那個把黑眼圈熬成華為工牌掛繩的倒霉蛋。過去100個夜晚&#xff0c;我在HarmonyOS NEXT的ArkWeb里被Origin:null反復按在地上摩擦——小程序白屏、OPTIONS 400、官方文檔沉默三連擊。最終&#xff0c;我用C、libcurl、OpenSSL和一堆速溶咖啡&am…