openpyxl默认全量解析XML构建DOM树导致CPU飙升;须启用read_only=True、values_only=True及显式关闭VBA等优化。

因为默认加载方式会把整个.xlsx解压、解析、建树、封装成对象,不是读数据,是在造内存炸弹。
openpyxl不加read_only=True会全量构建DOM树
Excel的.xlsx本质是ZIP包,含sharedStrings.xml、sheet1.xml等一堆XML。openpyxl默认模式下,不管你要不要样式或公式,它都会:
- 解压全部XML文件到内存
- 用
xml.etree.ElementTree逐个解析并构建完整DOM树 - 为每个Cell创建独立Python对象(含样式、字体、超链接等10+属性)
- 维护单元格间引用关系、共享字符串映射表、格式缓存
10万行 × 50列 = 至少500万个对象。这不是I/O瓶颈,是纯CPU+内存双杀。用strace -e trace=clone,execve能看到大量XML解析调用,top里Python进程CPU跑满但内存增长反而慢——说明卡在解析逻辑里,不是在搬数据。
pandas.read_excel()底层仍走openpyxl全量路径
很多人以为加了engine="openpyxl"就“优化”了,其实只是换了引擎,没换加载逻辑。pandas会在openpyxl加载完后,再做三件高开销的事:
立即学习“Python免费学习笔记(深入)”;
- 对每列执行
infer_dtype:混合类型列(如数字+字符串+空)会退化为object,并逐单元格调用isinstance() - 扫描所有列来推断
na_values,若你设了正则表达式,每行每列都re.search()一遍 - 重建索引、填充缺失结构、转换为DataFrame内部块(BlockManager)
即使只取一列,usecols参数也必须配合dtype显式指定,否则pandas仍会解析所有列来完成类型推测。
后台GC和对象生命周期失控
openpyxl每个Cell都是带引用计数的Python对象。高频创建/销毁时,CPython分代GC频繁触发,表现为CPU占用忽高忽低、处理卡顿。这不是代码写错,是库的设计取舍:openpyxl优先保功能完整(样式、公式、VBA),不保内存/CPU友好。
-
values_only=True可跳过Cell对象构造,直接返回元组,省掉90%对象开销 -
keep_vba=False(默认True)必须显式关闭,VBA解析极其耗CPU - 用完务必调用
wb.close(),否则底层C结构体无法被GC及时回收
Excel文件本身埋着隐形性能雷区
很多“大文件”实际有效数据很少,但因人工操作引入大量冗余负担:
- 整列设为日期格式,但只填了前10行 → openpyxl仍要为剩余10万行预分配格式对象
- 存在大量空行、合并单元格、条件格式区域 → 解析分支爆炸,CPU雪崩
- 嵌入图片、图表、OLE对象 → ZIP解压后体积翻倍,XML节点数激增
真正该先问的不是“怎么读得快”,而是“这文件为什么这么大”。打开zipinfo large_file.xlsx看看xl/media/和xl/charts/占多少,比调参管用得多。


















