资讯动态

Apache Fesod 大数据量 Excel 文件读取:共享字符串缓存策略与内存配置调优

发布时间:2026/10/4 14:51:10 来源:尧图企业网站定制
后端【免费下载链接】fesodFast. Easy. Done. Processing spreadsheets without worrying about large files causing OOM.项目地址https://gitcode.com/gh_mirrors/fast/fesod点击查看免费下载本文基于 Apache FesodIncubating官方文档 website/docs/sheet/help/large-data.md对应中文版 website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/help/large-data.md展开结合 fesod-sheet 模块源码与测试系统讲解 FesodSheet 在读取大体积 Excel 文件时的内存治理原理、默认缓存策略、两种自定义内存配置路线以及maxCacheActivateSize命中率调优方法。读完本文你将能够为低并发中小文件与高并发超大文件两类典型场景分别配置读取缓存让超大电子表格在几十 MB 级常驻内存内完成解析避免 OOM。背景为什么 Excel 大文件会撑爆内存Excel 03 与 Excel 07 的差异当需要读取 10MB 大小以上的文件时微软 Excel 03 格式XLS本身无法很好地处理相对内存占用会大很多。而 Excel 07 引入的 XLSXOpen XML格式中有一个非常关键的概念——共享字符串表Shared Strings工作簿中所有单元格的字符串文本被集中存放一份单元格通过索引引用它。这个设计能压缩文件体积但会显著放大内存开销如果把共享字符串全部加载进内存占用大约是电子表格文件大小的 3 到 10 倍。对于几十 MB 的 XLSX 文件意味着几百 MB 甚至更多的堆内存被一张字符串表吃掉这正是大文件读取 OOM 的根源。FesodSheet 的核心思路先落盘再反序列化针对上述问题FesodSheet 采用先存储文件再反序列化读取的策略来节约内存共享字符串不一次性全部驻留堆内存而是按需从临时文件/磁盘缓存中加载。代价是效率会有所降低。官方文档给出的经验是经过文件反序列化后读取效率大约降低 30%50%该数值并不固定取决于缓存命中率个别场景甚至可能超过 100%。也就是说这一策略是用一部分吞吐换内存安全适合文件大、堆内存有限的读取场景。如果对默认的读取效率尚可接受直接用默认配置即可读取单个电子表格的整个过程永久占用内存一般不会超过 50MB大概率在 30MB 左右其余临时对象会被垃圾回收器很快回收。默认策略自动判断5MB 是内存与文件的切换阈值FesodSheet 对大文件处理默认采用自动判断机制不需要用户干预共享字符串数据量存储方式内存占用估算5MB 以下内存存储MapCache约 1550MB超过 5MB文件存储Ehcache 磁盘缓存额外 20MB 用于存放临时共享字符串其余对象内存可预估约 10MB因此默认大约 30MB 的常驻内存就能读取一个超级大的文件5MB 阈值以下走内存超过 5MB 落盘落盘后还要预留 20MB 内存给活动批缓存细节见下文源码分析。源码视角缓存接口、两种实现与选择器为了让默认策略和后续自定义配置有据可依我们先看 fesod-sheet 模块的缓存体系。所有缓存实现都遵循统一的 ReadCache 接口它定义了 5 个生命周期方法init(AnalysisContext)初始化缓存put(String value)自动生成从 0 开始的 key 并写入缓存get(Integer key)按索引取值putFinished()全部写入完成后回调destroy()读取结束后清理资源。MapCache全内存实现MapCache 内部就是一个ListString所有共享字符串直接放内存。源码注释明确写道直接把临时数据放进 map效率略高但非常耗内存Putting temporary data directly into a map is a little more efficient but very memory intensive。它适合文件不大、追求吞吐的场景。Ehcache磁盘为主、内存为辅的实现Ehcache 是默认大文件缓存实现关键设计均可在源码中直接确认BATCH_COUNT 100共享字符串按100 条一批写入磁盘文件缓存fileCache配置为最多 20GB 磁盘空间临时目录由 FileUtils.createCacheTmpFile() 创建内存侧维护一个活动缓存activeCache默认保留 20 个批次条目对应SimpleReadCacheSelector的默认maxCacheActivateBatchCount 20即约 20 × 100 2000 条共享字符串常驻内存官方文档面向用户描述为1000 条左右的经验值两者数量级一致get(key)时先按key / 100定位批次并查活动缓存命中直接返回未命中cache miss则从磁盘文件缓存读取该批并换入活动缓存put与get在 debug 日志级别下分别输出Already put :{数量}与Cache misses count:{数量}这是下文调优判定的数据来源。SimpleReadCacheSelector自动选择器SimpleReadCacheSelector 是默认缓存选择器其readCache(PackagePart)逻辑非常直接读取共享字符串表部件sharedStrings.xml的字节大小若大小小于阈值maxUseMapCacheSize默认 5MB即DEFAULT_MAX_USE_MAP_CACHE_SIZE 5返回new MapCache()否则返回new Ehcache(maxCacheActivateSize, maxCacheActivateBatchCount)。此外还有一个 EternalReadCacheSelector固定返回用户传入的某个ReadCache它是readCache(...)参数背后的实现。readCache 与 readCacheSelector 只能二选一在 ReadWorkbookHolder 的构造逻辑中可以看到硬性校验readCache与readCacheSelector同时设置会抛出ExcelAnalysisException(readCache and readCacheSelector only one choice.)。两者都不设置时默认走new SimpleReadCacheSelector()。对应地ExcelReaderBuilder 提供readCache(ReadCache)与readCacheSelector(ReadCacheSelector)两个链式方法二选一使用。自定义内存配置两条典型路线如果想自定义设置首先要确定愿意为读取一个超级大电子表格分配多少内存指读取过程中的永久占用新生代立即回收的临时对象不计。官方给出了一个规划示例希望读取过程最多占用100MB内存那么设置使用文件存储的共享字符串大小阈值为20MB小于 20MB 存内存大于则落临时文件再设置文件存储时临时共享字符串占用内存约90MB即 20MB 90MB ≈ 100MB。路线一低并发、中小文件、内存充足 → 强制全内存 MapCache适用前提最大文件条数也就十几二十万条、电子表格只有十几二十 MB、不会有很高的并发、且机器内存较大。// 强制使用内存存储这样一个 20M 的电子表格大约占用 150M 内存 // 其中大量是临时对象约 100M 会被持续 GC // 效率比下面复杂的文件缓存策略高很多 // 注意这里只是加了 readCache(new MapCache()) 一个参数其余用法参照普通读取示例 FesodSheet.read() .readCache(new MapCache());官方评价这比复杂的落盘策略高效得多代价是内存占用大幅上升——一个 20M 的电子表格约消耗 150M 内存大量临时对象意味着 100M 会被持续 GC。该用法同样出现在仓库测试 ParameterDataTest.java 中.readCache(new MapCache())可作为可运行参考。路线二高并发、经常处理超大文件 → SimpleReadCacheSelector 文件缓存适用前提并发要求较高且经常处理超级大文件。// 第一个参数共享字符串达到多少 MB 后改用文件存储单位 MB默认 5M // 第二个参数文件存储时内存中最多存放多少 MB 的缓存数据默认 20M // 例如希望用 100M 内存解析过程中的永久占用临时对象不计解析电子表格 // 按前面计算约 20M 90M所以参数设置为20 和 90 // 注意这里只是加了 readCacheSelector(new SimpleReadCacheSelector(5, 20)) 一个参数 // 其余用法参照普通读取示例 FesodSheet.read() .readCacheSelector(new SimpleReadCacheSelector(5, 20));结合源码补充说明参数含义maxUseMapCacheSize第一个参数共享字符串部件小于该值MB时用MapCache大于等于则用Ehcache默认 5maxCacheActivateBatchCount第二个参数对应的新式参数文件存储时内存中最多保留多少批共享字符串默认 20 批。批与条数的换算关系见 Ehcache.BATCH_COUNT 100即默认约 2000 条常驻内存。完整可运行的读取链结合官方入门文档 website/docs/sheet/read/simple.md 的常规写法// 示例高并发超大文件场景允许 100M 常驻内存 FesodSheet.read(file, listener) .head(DemoData.class) .sheet() // 读取第一个 sheet .readCacheSelector(new SimpleReadCacheSelector(20, 90)) .doRead();提示若需要同步拿到全部数据可使用doReadAllSync()见 ExcelReaderBuilder大文件场景更推荐监听器模式逐行消费避免一次性堆积。关于 maxCacheActivateSize命中率判定与参数演进为什么不能太小也不能太大FesodSheet 使用文件存储时会把共享字符串拆分成若干批文档表述为约1000 条一批从源码看实际是BATCH_COUNT 100条/批默认活动缓存保留 20 批写入文件存储。电子表格读取共享字符串大概率按顺序访问所以默认把最新的一批约 10002000 条对应约 20MB放在内存命中后直接返回未命中才去读文件。因此该值不能设置太小太小难以命中会一直读文件导致效率骤降设置太大则会过度占用内存。用 debug 日志量化命中率要判断maxCacheActivateSize是否需要调整开启 debug 日志后有两个关键输出来源见 Ehcache.java 的日志埋点Already put :{值}已写入缓存的总条数取最后一次输出。例如Already put :4000000表示约400 万条共享字符串Cache misses count:{值}未命中次数。例如Cache misses count:4001表示约4000次未命中。官方给出的判定方法400万 / 4000 1000即总写入条数 / 未命中次数得到每次未命中之间平均间隔的条数。结果为 1000 左右说明maxCacheActivateSize已非常合理活动缓存恰好覆盖了最近 1000 条的顺序访问模式小于 500 则问题非常大说明频繁换批、大量读文件5001000 之间可以接受。参数演进maxCacheActivateSize 已废弃需要特别提醒在 SimpleReadCacheSelector 源码中maxCacheActivateSize单位 MB字段已被标记Deprecated注释明确建议请使用maxCacheActivateBatchCount控制占用内存大小。二者在 Ehcache 构造中的关系是传入maxCacheActivateSize旧活动缓存按heap(大小, MemoryUnit.MB)配置即以 MB 为单位的堆内存上限传入maxCacheActivateBatchCount新活动缓存按heap(批数, EntryUnit.ENTRIES)配置即以批为单位的条目上限每批 100 条默认 20 批。新代码建议直接使用SimpleReadCacheSelector(maxUseMapCacheSize, maxCacheActivateBatchCount)这样的双参构造或通过 setter 设置maxCacheActivateBatchCount用批数而非MB来精确控制活动缓存规模。测试佐证与延伸阅读仓库测试 ParameterDataTest.java 中多处使用readCache(new MapCache())配合FesodSheet.read(file, listener).head(...).mandatoryUseInputStream(Boolean.FALSE).autoCloseStream(Boolean.TRUE)的完整链路进行读写往返校验验证了强制内存缓存路径的可用性。关于大文件读取的更多实践监听器逐行消费、避免全量加载的用法website/docs/sheet/read/simple.md大数据场景常见问题排查website/docs/sheet/help/faq.md本文所述内存策略的源码实现ReadCache、MapCache、Ehcache、SimpleReadCacheSelector。小结面对文件比堆内存还大的读取需求FesodSheet 用一套内存缓存 磁盘缓存的混合策略把常驻内存压到 30MB 量级5MB 阈值以下走全内存MapCache以上走Ehcache落盘并按批换入内存。实际使用时只需在readCache与readCacheSelector之间二选一文件小、并发低、内存足readCache(new MapCache())换取最高吞吐文件超大、并发高readCacheSelector(new SimpleReadCacheSelector(阈值, 批数/缓存MB))按预算规划内存调优时用 debug 日志的Already put与Cache misses count计算命中间距保持在 5001000 区间即为合理配置并优先采用新的maxCacheActivateBatchCount参数。赞分享后端【免费下载链接】fesodFast. Easy. Done. Processing spreadsheets without worrying about large files causing OOM.项目地址https://gitcode.com/gh_mirrors/fast/fesod点击查看免费下载相关推荐RedisJSON内存管理深度解析碎片整理、共享字符串与优化策略RedisJSON内存管理深度解析碎片整理、共享字符串与优化策略 RedisJSON作为Redis的JSON数据类型扩展为开发者提供了高效存储和操作JSONApache Ignite数据区域配置详解内存管理与缓存策略Apache Ignite数据区域配置详解内存管理与缓存策略 概述 Apache Ignite作为一款高性能的内存计算平台其核心功能之一就是高效的内存管理。分布式数据库缓存关系型数据库KV存储后端Triton内存管理完全解析共享内存与缓存策略Triton内存管理完全解析共享内存与缓存策略 Triton语言和编译器作为深度学习计算的关键基础设施其内存管理机制直接影响着GPU计算的性能表现。本文将深编译器编程语言人工智能深度学习高性能计算上一篇如何3分钟掌握暗黑3按键助手终极自动化游戏辅助完全指南下一篇夸克网盘自动化管理终极方案智能转存、文件整理与媒体库整合创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑