最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實(shí)現(xiàn)百萬(wàn)數(shù)據(jù)導(dǎo)出Excel的詳細(xì)指南

 更新時(shí)間:2025年07月16日 08:26:07   作者:天天摸魚(yú)的java工程師  
這篇文章主要為大家詳細(xì)介紹了使用Java語(yǔ)言實(shí)現(xiàn)百萬(wàn)數(shù)據(jù)導(dǎo)出Excel的詳細(xì)方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

本文分享一個(gè)Java開(kāi)發(fā)者從初出茅廬到技術(shù)老手的成長(zhǎng)歷程,聚焦百萬(wàn)級(jí)數(shù)據(jù)導(dǎo)出場(chǎng)景,看如何從OOM崩潰走向優(yōu)雅解決。

一、新手踩坑:我的第一個(gè)導(dǎo)出功能

剛?cè)胄心悄?,我接到第一個(gè)獨(dú)立任務(wù):實(shí)現(xiàn)訂單數(shù)據(jù)導(dǎo)出Excel。當(dāng)時(shí)憑著學(xué)校學(xué)的基礎(chǔ)知識(shí),寫(xiě)出了這樣的代碼:

// 新手版導(dǎo)出代碼 - 內(nèi)存炸彈!
public void exportOrders(HttpServletResponse response) {
    // 1. 一次性加載全量數(shù)據(jù)
    List<Order> allOrders = orderDao.findAll(); 
    
    // 2. 創(chuàng)建Excel對(duì)象(當(dāng)時(shí)還不知道內(nèi)存代價(jià))
    Workbook workbook = new HSSFWorkbook();
    Sheet sheet = workbook.createSheet("Orders");
    
    // 3. 逐行填充數(shù)據(jù)
    int rowNum = 0;
    for (Order order : allOrders) {  // 百萬(wàn)次循環(huán)
        Row row = sheet.createRow(rowNum++);
        row.createCell(0).setCellValue(order.getId());
        row.createCell(1).setCellValue(order.getAmount());
        // ...15+個(gè)字段
    }
    
    // 4. 寫(xiě)入響應(yīng)流
    workbook.write(response.getOutputStream());
}

第一次壓測(cè)時(shí)的災(zāi)難現(xiàn)場(chǎng)

Exception in thread "http-nio-8080-exec-3" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3332)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
    // 堆棧指向Excel對(duì)象創(chuàng)建

二、錯(cuò)誤分析:為什么新手代碼會(huì)OOM

三重內(nèi)存炸彈

  • 數(shù)據(jù)對(duì)象駐留內(nèi)存:百萬(wàn)條Order對(duì)象(約1.2GB)
  • Excel DOM樹(shù)爆炸:POI的HSSFWorkbook每個(gè)單元格都是獨(dú)立對(duì)象
  • 字符串拼接黑洞:字段值拼接消耗額外內(nèi)存

內(nèi)存消耗估算

組件1萬(wàn)條10萬(wàn)條100萬(wàn)條
訂單對(duì)象120MB1.2GB12GB
Excel對(duì)象(估算)200MB2GB20GB+
總內(nèi)存320MB3.2GB32GB+

當(dāng)時(shí)我用的測(cè)試機(jī)只有2G內(nèi)存...

三、解決之道:流式處理方案

架構(gòu)演進(jìn)對(duì)比

graph LR
    A[新手方案] -->|全內(nèi)存| B[OOM崩潰]
    C[優(yōu)化方案] -->|磁盤(pán)緩沖| D[成功導(dǎo)出]
    D -->|內(nèi)存控制| E[穩(wěn)定運(yùn)行]

核心代碼改造(基于POI SXSSF)

public void streamExport(HttpServletResponse response) throws Exception {
    // 1. 創(chuàng)建流式工作簿(內(nèi)存中只保留100行)
    Workbook workbook = new SXSSFWorkbook(100); 
    Sheet sheet = workbook.createSheet("訂單數(shù)據(jù)");
    
    // 2. 寫(xiě)表頭
    Row header = sheet.createRow(0);
    header.createCell(0).setCellValue("ID");
    // ...其他表頭
    
    // 3. 分頁(yè)查詢(xún)+流式寫(xiě)入
    int pageSize = 2000;
    int pageNum = 1;
    int rowIndex = 1; // 數(shù)據(jù)行起始位置
    
    while (true) {
        // 4. 分頁(yè)查詢(xún)(避免全量加載)
        List<Order> page = orderDao.findByPage(pageNum, pageSize);
        if (page.isEmpty()) break;
        
        // 5. 批量寫(xiě)入當(dāng)前頁(yè)
        for (Order order : page) {
            Row row = sheet.createRow(rowIndex++);
            row.createCell(0).setCellValue(order.getId());
            // ...其他字段
        }
        
        // 6. 刷新當(dāng)前頁(yè)數(shù)據(jù)到磁盤(pán)
        ((SXSSFSheet)sheet).flushRows(page.size());
        
        pageNum++;
    }
    
    // 7. 設(shè)置響應(yīng)頭
    response.setContentType("application/vnd.ms-excel");
    response.setHeader("Content-Disposition", "attachment;filename=orders.xlsx");
    
    // 8. 流式輸出到客戶端
    workbook.write(response.getOutputStream());
    
    // 9. 清理臨時(shí)文件
    ((SXSSFWorkbook)workbook).dispose();
}

四、關(guān)鍵技術(shù)解析

1.SXSSFWorkbook 核心機(jī)制

滑動(dòng)窗口:內(nèi)存中只保留指定行數(shù)(默認(rèn)100行)

自動(dòng)刷盤(pán):超過(guò)窗口大小的行寫(xiě)入磁盤(pán)臨時(shí)文件

內(nèi)存對(duì)比:傳統(tǒng)方式 vs SXSSF

// 傳統(tǒng)方式(危險(xiǎn)?。?
Workbook workbook = new XSSFWorkbook(); 

// 流式處理(安全)
Workbook workbook = new SXSSFWorkbook(100); 

2.分頁(yè)查詢(xún)優(yōu)化技巧

避免深度分頁(yè):不要使用limit 1000000,100

推薦方案:基于ID范圍的連續(xù)分頁(yè)

SELECT * FROM orders WHERE id > ? ORDER BY id LIMIT 2000

3.內(nèi)存監(jiān)控技巧

添加JVM參數(shù)觀察內(nèi)存變化:

-XX:+PrintGCDetails -Xloggc:gc.log

五、性能優(yōu)化實(shí)戰(zhàn)

樣式處理陷阱

// 錯(cuò)誤做法:每行創(chuàng)建樣式(內(nèi)存爆炸)
for(Order order : orders) {
    CellStyle style = workbook.createCellStyle();
    row.setCellStyle(style);
}

// 正確做法:樣式池復(fù)用
CellStyle moneyStyle = workbook.createCellStyle();
moneyStyle.setDataFormat(BuiltinFormats.getBuiltinFormat(4));

// 在需要時(shí)直接使用
cell.setCellStyle(moneyStyle);

臨時(shí)文件管理

// 自定義臨時(shí)文件位置(避免/tmp爆滿)
File tmpDir = new File("/data/tmp");
SXSSFWorkbook workbook = new SXSSFWorkbook(null, 100, true, tmpDir);

寫(xiě)入加速技巧

// 批量設(shè)置單元格值(減少方法調(diào)用)
Row row = sheet.createRow(0);
Object[] values = {"ID", "金額", "日期"};
for (int i = 0; i < values.length; i++) {
    row.createCell(i).setCellValue(values[i].toString());
}

六、方案效果對(duì)比

指標(biāo)新手方案流式方案
內(nèi)存占用>3GB (OOM)≈150MB
響應(yīng)時(shí)間無(wú)法完成5分鐘/百萬(wàn)行
CPU占用頻繁Full GC平穩(wěn)
代碼復(fù)雜度簡(jiǎn)單中等
可支持?jǐn)?shù)據(jù)量<1萬(wàn)行>1000萬(wàn)行

七、避坑指南:血淚經(jīng)驗(yàn)

分頁(yè)查詢(xún)的坑

// MySQL深度分頁(yè)性能陷阱
List<Order> list = orderDao.query("SELECT * FROM orders LIMIT 900000,1000");

資源關(guān)閉的坑

// 忘記關(guān)閉資源導(dǎo)致內(nèi)存泄漏
Workbook workbook = new SXSSFWorkbook();
// 必須添加finally塊關(guān)閉

數(shù)據(jù)類(lèi)型的坑

// 日期類(lèi)型特殊處理
CellStyle dateStyle = workbook.createCellStyle();
dateStyle.setDataFormat(workbook.createDataFormat().getFormat("yyyy-MM-dd"));
cell.setCellStyle(dateStyle);

八、老鳥(niǎo)的思考

8年Java開(kāi)發(fā)教會(huì)我處理海量數(shù)據(jù)的核心原則:

內(nèi)存有限性原則

graph LR
內(nèi)存-->磁盤(pán)-->分布式

當(dāng)內(nèi)存不夠時(shí),合理利用磁盤(pán)空間

流式處理三要素

  • 分頁(yè)加載(Paging)
  • 批量處理(Batching)
  • 即時(shí)釋放(Releasing)

資源管理箴言

"打開(kāi)的資源要及時(shí)關(guān)閉,創(chuàng)建的對(duì)象要明確生命周期"

最后建議:超過(guò)500萬(wàn)行數(shù)據(jù)建議使用CSV格式或?qū)I(yè)ETL工具,Excel畢竟不是數(shù)據(jù)庫(kù)!

以上就是Java實(shí)現(xiàn)百萬(wàn)數(shù)據(jù)導(dǎo)出Excel的詳細(xì)指南的詳細(xì)內(nèi)容,更多關(guān)于Java百萬(wàn)數(shù)據(jù)導(dǎo)出Excel的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

  • new出來(lái)的對(duì)象中無(wú)法使用@autowired進(jìn)行對(duì)象bean注入問(wèn)題

    new出來(lái)的對(duì)象中無(wú)法使用@autowired進(jìn)行對(duì)象bean注入問(wèn)題

    這篇文章主要介紹了基于new出來(lái)的對(duì)象中無(wú)法使用@autowired進(jìn)行對(duì)象bean注入問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Spring IOC創(chuàng)建對(duì)象的兩種方式

    Spring IOC創(chuàng)建對(duì)象的兩種方式

    這篇文章主要給大家介紹了關(guān)于Spring IOC創(chuàng)建對(duì)象的兩種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java?面向?qū)ο缶幊讨衧tatic關(guān)鍵字的深度剖析與實(shí)踐

    Java?面向?qū)ο缶幊讨衧tatic關(guān)鍵字的深度剖析與實(shí)踐

    在?Java?面向?qū)ο缶幊痰凝嫶篌w系中,?static關(guān)鍵字作為一項(xiàng)極為重要的特性,對(duì)代碼的設(shè)計(jì)、組織和性能有著深遠(yuǎn)的影響,本文將深入探討static關(guān)鍵字在靜態(tài)變量、靜態(tài)方法和工具類(lèi)設(shè)計(jì)中的應(yīng)用,并剖析其中的關(guān)鍵要點(diǎn)與注意事項(xiàng),感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • 初識(shí)sa-token及登錄授權(quán)簡(jiǎn)單實(shí)現(xiàn)

    初識(shí)sa-token及登錄授權(quán)簡(jiǎn)單實(shí)現(xiàn)

    這篇文章主要為大家介紹了sa-token及登錄授權(quán)簡(jiǎn)單實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 使用postman傳遞list集合后臺(tái)springmvc接收

    使用postman傳遞list集合后臺(tái)springmvc接收

    這篇文章主要介紹了使用postman傳遞list集合后臺(tái)springmvc接收的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • java圖片縮放實(shí)現(xiàn)圖片填充整個(gè)屏幕

    java圖片縮放實(shí)現(xiàn)圖片填充整個(gè)屏幕

    這篇文章主要介紹了java圖片縮放實(shí)現(xiàn)圖片填充整個(gè)屏幕,本文提供了兩種解決方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Java創(chuàng)建型設(shè)計(jì)模式之工廠方法模式深入詳解

    Java創(chuàng)建型設(shè)計(jì)模式之工廠方法模式深入詳解

    工廠方法模式(FACTORY METHOD)是一種常用的類(lèi)創(chuàng)建型設(shè)計(jì)模式,此模式的核心精神是封裝類(lèi)中變化的部分,提取其中個(gè)性化善變的部分為獨(dú)立類(lèi),通過(guò)依賴(lài)注入以達(dá)到解耦、復(fù)用和方便后期維護(hù)拓展的目的。它的核心結(jié)構(gòu)有四個(gè)角色,分別是抽象工廠、具體工廠、抽象產(chǎn)品、具體產(chǎn)品
    2022-09-09
  • Kotlin語(yǔ)法學(xué)習(xí)-變量定義、函數(shù)擴(kuò)展、Parcelable序列化等簡(jiǎn)單總結(jié)

    Kotlin語(yǔ)法學(xué)習(xí)-變量定義、函數(shù)擴(kuò)展、Parcelable序列化等簡(jiǎn)單總結(jié)

    這篇文章主要介紹了Kotlin語(yǔ)法學(xué)習(xí)-變量定義、函數(shù)擴(kuò)展、Parcelable序列化等簡(jiǎn)單總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • java實(shí)現(xiàn)檢測(cè)Linux網(wǎng)絡(luò)狀態(tài)(附源碼)

    java實(shí)現(xiàn)檢測(cè)Linux網(wǎng)絡(luò)狀態(tài)(附源碼)

    實(shí)時(shí)監(jiān)控服務(wù)器網(wǎng)絡(luò)狀態(tài)是保證業(yè)務(wù)高可用性和可靠性的重要環(huán)節(jié),本文主要介紹了如何設(shè)計(jì)并實(shí)現(xiàn)一個(gè)高性能,可擴(kuò)展,支持事件訂閱與周期采樣的Linux?網(wǎng)絡(luò)狀態(tài)檢測(cè)工具,需要的可以了解下
    2025-06-06
  • 最新評(píng)論

    高州市| 姚安县| 沂水县| 镇赉县| 兴义市| 肇源县| 泽库县| 高雄市| 平凉市| 葵青区| 朝阳区| 日照市| 郑州市| 石城县| 长海县| 固阳县| 乐都县| 洛隆县| 涡阳县| 阿城市| 思南县| 娄底市| 葵青区| 互助| 郧西县| 永平县| 乌拉特前旗| 湛江市| 靖州| 呼和浩特市| 巨野县| 仪陇县| 云安县| 赤峰市| 犍为县| 哈尔滨市| 城固县| 尼勒克县| 蛟河市| 浦北县| 洛川县|