You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么pandas.to_parquet保存DataFrame时内存占用远超数据实际大小?

问题根因
  • sys.getsizeof的测算结果完全不具备参考性:对于存储为object dtype的pandas字符串列,sys.getsizeof只会统计DataFrame结构本身、以及列里存的Python对象指针的内存,不会递归统计指针指向的、存在堆上的实际字符串内容的内存。7300万行的英文句子列,按平均每句80-120字符算,仅原始字符串的实际内存占用就有60-90GB,远高于你测出来的25GB。
  • 两款parquet引擎写入时都会产生多份全量内存副本:不管是pyarrow还是fastparquet,写入前都需要把pandas的object类型字符串转换成引擎自有的原生字符串存储结构,这个转换过程会生成一份和全量字符串等大的内存副本;默认写入逻辑下,引擎会先把全量数据转换完成后再统一做编码、写入,光原始数据+转换副本的内存占用就会达到实际数据大小的2倍以上。
  • fastparquet直接抛溢出错误是历史遗留问题:fastparquet的字符串转换逻辑长期使用32位有符号整数做缓冲区长度计数,当单批处理的字符串总长度超过2^31-1字节(约2.1GB)时就会触发整数溢出,直接报错,根本走不到后续压缩、写入流程,这也是你改压缩参数完全不影响报错表现的原因——OOM和溢出都发生在压缩步骤之前,压缩参数还没生效就已经失败了。
  • 默认行组配置进一步放大内存开销:pyarrow默认的行组大小通常在百万行级以上,每个行组做字典编码、RLE编码、压缩时还需要申请额外的编码缓冲区内存,叠加上前面的副本开销,总内存占用突破512GB是完全符合逻辑的。
验证与修复方案
  • 先拿到真实内存数据:用df.memory_usage(deep=True).sum()测算DataFrame实际内存占用,加了deep=True参数才会递归统计字符串等引用类型对象的真实内存,测完你就能看到实际大小远高于25GB。
  • 替换字符串存储类型:提前把两列都转成pandas原生的string类型,执行df = df.astype({'文本列名':'string', 'ID列名':'string'}),这种格式下字符串是连续存储在原生数组里的,不是分散的Python对象,转parquet引擎格式时不需要遍历拷贝大量零散Python对象,转换阶段的内存开销可以降低60%以上。
  • 调小行组大小:调用to_parquet时传入row_group_size=100000(即10万行一个行组),降低单批次编码、压缩的缓冲区内存占用。
  • 超大数据集改用流式分批写入:如果调整上面两个参数后还是有内存压力,不要直接用DataFrame的to_parquet接口,改用对应引擎的流式写入器(比如pyarrow的ParquetWriter),把DataFrame按100-500万行的粒度切分,逐批写入、逐批释放内存,全程不会生成全量数据副本,内存占用可以控制在单批大小的1.5倍以内。

内容的提问来源于stack exchange,提问作者Tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 06:18:16