ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

一次线上OOM排查:Apache Fesod如何搞定百万行Excel

一次线上OOM排查:Apache Fesod如何搞定百万行Excel 一次线上OOM排查Apache Fesod如何搞定百万行Excel【免费下载链接】fesodFast. Easy. Done. Processing spreadsheets without worrying about large files causing OOM.项目地址: https://gitcode.com/gh_mirrors/fast/fesod凌晨2:17告警群炸了导出服务 Java heap space。罪魁祸首是一份108万行的明细表——服务一解析就OOM怎么调内存都不行。用Java处理大Excel内存是绕不过去的坎。当晚我改用 Apache Fesod 的流式读取重写了这段逻辑问题当天就解决。这篇复盘记录排查过程也给你能直接抄走的写法。深夜的heap space大Excel是怎么把服务拖垮的问题代码长这样——几乎每个初接触POI的人都会这么写Workbook workbook WorkbookFactory.create(new File(daily.xlsx)); Sheet sheet workbook.getSheetAt(0); for (Row row : sheet) { // 逐行处理... }WorkbookFactory.create走的是POI的UserModel模型它会在内存里把整张表重建一遍每个单元格都是一个Java对象。100万行×20列就是几千万个对象躺在堆里。别忘了共享字符串表Shared Strings——Excel 2007之后所有重复文本都集中存在这张表里官方文档给出的数字是如果把它全部加载进内存占用约为表格文件大小的3到10倍。一个50MB的文件JVM吃进500MB很正常。所以问题从来不是数据多而是加载方式。排查现场把重建整张表换成路过每一行Excel文件本质是一个zip包里面是成串的XML。既然要读的只是数据为什么要先在内存里搭一个完整的对象模型顺着XML流读一遍、读完一行丢一行内存占用就和文件大小彻底脱钩只和当前正在处理的这一行有关——这就是流式SAX风格解析。Apache Fesod 走的正是这条路而且对共享字符串的处理更彻底默认5MB以内的共享字符串留在内存超过5MB的部分拆成每1000条一批落到临时文件命中不了再从文件读。官方文档的结论是读一个超大文件全程常驻内存一般不超过50MB多数时候在30MB左右。代价是反序列化会让读取效率下降30%~50%但换来的是文件多大都不怕。突破口两段代码从读不进到读不完Apache Fesod 不是新造轮子——它是易ExcelEasyExcel作者团队的升级之作2025年9月正式进入Apache孵化器项目定位一句话说清处理电子表格时不用担心大文件导致OOM。上手只需三步加依赖、写监听器、调用读取。Maven坐标如下JDK 8到25都支持dependency groupIdorg.apache.fesod/groupId artifactIdfesod-sheet/artifactId version2.0.2-incubating/version /dependency核心是那个监听器——每解析一行调一次invoke攒够100行批量落一次库读完后doAfterAllAnalysed兜底清空缓存public class DemoDataListener implements ReadListenerDemoData { private static final int BATCH_COUNT 100; private ListDemoData cache new ArrayList(BATCH_COUNT); Override public void invoke(DemoData data, AnalysisContext context) { cache.add(data); if (cache.size() BATCH_COUNT) { dao.save(cache); // 攒够一批落库 cache.clear(); // 立刻清空内存不积累 } } Override public void doAfterAllAnalysed(AnalysisContext context) { dao.save(cache); // 处理剩余不足一批的数据 } }调用就三行读第一个sheetFesodSheet.read(fileName, DemoData.class, new DemoDataListener()) .sheet() .doRead();说白了你只管在invoke里消费每一行内存里永远只有100条数据。那个108万行的文件我用这套写法重跑堆内存稳如老狗。反向操作导出百万行写入端也别硬扛读的问题解决了别忘了导出的另一半。一次性doWrite100万条数据照样会炸。正确姿势是拿到ExcelWriter后分批写——每批100行、循环1000次内存里永远只有一批再给底层SXSSFWorkbook开启临时文件压缩省磁盘代价是少量CPUtry (ExcelWriter writer FesodSheet.write(fileName, DemoData.class) .registerWriteHandler(new WorkbookWriteHandler() { Override public void afterWorkbookCreate(WorkbookWriteHandlerContext context) { Workbook wb context.getWriteWorkbookHolder().getWorkbook(); if (wb instanceof SXSSFWorkbook) { ((SXSSFWorkbook) wb).setCompressTempFiles(true); } } }).build()) { WriteSheet sheet FesodSheet.writerSheet(数据导出).build(); for (int i 0; i 1000; i) { writer.write(nextBatch(), sheet); // 每次只写100行 } }记得用FileUtils.getPoiFilesPath()盯一下临时目录的磁盘占用压缩开关就是为这个场景准备的。用数字说话这份方案到底值不值得换把两种路线的内存特征摆在一起看对比项传统POI全量加载Apache Fesod流式读取共享字符串全载内存约占文件3~10倍超5MB自动落盘按1000条分批缓存常驻内存随文件大小线性增长约30MB上限50MB左右大文件表现几百万行即OOM文件多大都读得动但别急着全面替换如果文件就几MB、几万行以内默认的简单API完全够用没必要引入复杂策略。Fesod的默认行为本来就是自动判断——共享字符串小于5MB就留在内存不折腾你。顺带说一句社区认可度这个项目从2025年初几乎零星一年多涨到近6000星背后是实打实的用户在用。收尾三条能直接带走的经验回到那个凌晨。108万行的文件后来用Fesod跑通当天上线之后再没收到那条告警。复盘下来真正值钱的是这三条读大文件用监听器分批消费 共享字符串落盘策略永远别一次性doRead写大文件用ExcelWriter分批write SXSSF压缩临时文件内存和磁盘一起省小文件别杀鸡用牛刀默认策略会自动帮你做判断。现在就可以动手git clone https://gitcode.com/gh_mirrors/fast/fesod后仓库fesod-examples里 read、write、fill、web 各场景的示例代码都齐了照着BasicReadExample改改就能用。下一次再遇到文件太大读不动你就有答案了。【免费下载链接】fesodFast. Easy. Done. Processing spreadsheets without worrying about large files causing OOM.项目地址: https://gitcode.com/gh_mirrors/fast/fesod创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表