直接用to_parquet()会卡住或爆内存,因其默认将整个DataFrame加载进内存再序列化;解法是分块写入+pyarrow引擎+列式压缩+类型统一,并注意磁盘I/O瓶颈。

为什么直接用 to_parquet() 会卡住或爆内存?
因为默认的 to_parquet() 会把整个 DataFrame 加载进内存再序列化,遇到几十 GB 的数据时,Python 进程很容易被系统 OOM kill,或者写出极慢(尤其在单核机器上)。这不是函数写得不好,而是它默认面向中等规模数据设计。
关键解法是分块 + 列式压缩 + 合理引擎选择:
-
engine="pyarrow"必须启用(fastparquet对超大表支持弱,且不兼容部分 Pandas 类型) - 用
partition_cols拆分目录结构(如按日期、ID范围),避免单文件过大 - 显式控制
chunksize(虽然to_parquet()本身不接受该参数,但你可以手动切片 + 循环写入)
怎么安全地分块写入 Parquet?
别依赖 Pandas 自动分块——它没这个机制。你需要自己切片,配合 append=True 写入同一路径(注意:仅 PyArrow 支持追加,且目标必须是已存在的 Parquet dataset)。
实操建议:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 先用
df.iloc[0:1].to_parquet("output/", engine="pyarrow", index=False)创建空 schema 目录 - 循环中每次取
df.iloc[i:i+500000](具体大小看内存余量,50 万行通常较稳) - 每块调用
to_parquet("output/", engine="pyarrow", index=False, append=True) - 务必加
use_dictionary=True和compression="snappy"(平衡速度与体积)
示例片段:
import pandas as pd
for i in range(0, len(df), 500000):
chunk = df.iloc[i:i+500000]
chunk.to_parquet(
"output/",
engine="pyarrow",
index=False,
use_dictionary=True,
compression="snappy",
append=True
)
导出后为什么读不出来或类型错乱?
常见于字符串列含混合类型(如部分为 None、部分为 bytes)、或时间列时区未归一。PyArrow 对 schema 一致性极其敏感,第一次写入时定下的类型,后续所有块都必须严格匹配。
预防措施:
- 导出前强制统一列类型:
df["col"] = df["col"].astype("string")(Pandas 1.3+ 推荐)或df["col"] = df["col"].astype(str) - 时间列统一转为
datetime64[ns, UTC],避免本地时区混入 - 空值列用
pd.NA而非None或np.nan(尤其 string/boolean 类型) - 导出后立刻用
pd.read_parquet("output/", engine="pyarrow")抽样验证前几行
还有更快的替代方案吗?
如果单机处理仍太慢,说明已超出 Pandas 承载边界。这时应跳过 Pandas 中间层,改用更底层的流式写法:
- 用
pyarrow.parquet.ParquetWriter直接写 record batch,绕过 DataFrame 构建开销 - 对超大 CSV/数据库源,考虑用
dask.dataframe或polars替代:它们原生支持分块 Parquet 写入,且调度更高效 - 若数据来自数据库,优先在服务端导出(如 DuckDB 的
COPY ... TO 'x.parquet' (FORMAT PARQUET))
最易忽略的一点:磁盘 I/O 比 CPU 更可能成为瓶颈。确保输出路径在 SSD 上,且不要和系统盘共用同一物理设备。

















