pyarrow write_table 是批量转 parquet 最稳妥方式,需显式指定 schema、启用字典编码、选用 zstd/snappy 压缩,并控制行组与文件粒度以提升压缩比和查询性能。

PyArrow write_table 是批量转 Parquet 的核心入口
直接用 pyarrow.parquet.write_table 写入单个文件最稳妥,它底层调用的是 Arrow 的原生 Parquet writer,支持所有压缩算法和分块策略。别用 pandas.DataFrame.to_parquet 批量跑——它每次都会重建 schema、隐式类型推断,容易在列类型不一致时出错,且无法精细控制 use_dictionary、data_page_size 等关键压缩参数。
实操建议:
- 先用
pyarrow.csv.read_csv读取 CSV,显式传入schema(避免自动推断偏差) - 对每个 CSV 文件,构造
pyarrow.Table后统一调用write_table - 压缩选
compression="ZSTD"或"SNAPPY":ZSTD 压缩比更高(尤其文本列),SNAPPY 解压更快;"UNCOMPRESSED"仅用于调试 - 务必设置
use_dictionary=True(默认开启),对重复字符串/枚举列压缩效果极明显
列类型不一致会导致 ArrowInvalid: Schema at position X was different
多个 CSV 文件字段名相同但实际数据类型不同(比如一个文件里 user_id 全是整数,另一个混了字符串),pyarrow.csv.read_csv 单独读不会报错,但合并或写入统一 Parquet 时 schema 校验失败。
解决办法不是强行转换,而是提前对齐:
- 用
pyarrow.csv.CSVReadOptions和pyarrow.csv.ParseOptions控制空值识别(如null_values=["", "NULL", "N/A"]) - 读取第一个 CSV 生成基准
schema,后续所有文件都用read_csv(..., schema=base_schema)强制适配 - 对疑似问题列(如时间、ID),手动指定
convert_options=pa.csv.ConvertOptions(column_types={"ts": pa.timestamp("s"), "id": pa.int64()})
小文件太多会拖慢查询,用 max_rows_per_group 和分区控制粒度
Parquet 不是“越小越好”。1000 个 50KB 的文件,比 1 个 50MB 的文件更难被 Spark/Presto 高效扫描——元数据开销大、IO 调度碎片化。
批量转换时主动聚合:
- 按业务逻辑分组(如按日期列
dt分区:partition_cols=["dt"]),让每个分区内文件数可控 - 单文件行数控制在 100 万~500 万之间,设
max_rows_per_group=500_000(影响 RowGroup 大小,也间接控制文件体积) - 禁用
write_table(..., use_legacy_dataset=False)下的自动分片,自己用pa.dataset.write_dataset精确控制输出路径和文件命名
磁盘节省没达到预期?检查 dictionary_encoding 是否真正生效
很多用户设了 use_dictionary=True 却没看到压缩提升,原因通常是:列里唯一值过多(> 百万级)、或字符串太长(平均 > 2KB),Arrow 会自动退化为 PLAIN 编码。
验证方式:
- 用
parquet-tools meta xxx.parquet查看 ColumnChunk 的encoding字段,确认是PLAIN_DICTIONARY而非PLAIN - 对高基数字符串列(如 UUID、URL),改用
compression="ZSTD"+data_page_size=1024*1024提升压缩率 - 数值列别盲目启字典编码——整型列用
PLAIN+ZSTD通常比PLAIN_DICTIONARY更省空间
真正的高压缩比依赖组合:类型对齐 + 字典编码 + ZSTD + 合理分块。少一个环节,磁盘节省就打七折。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











