fastparquet读parquet比pyarrow慢,因其默认不启用多线程解码和列裁剪,且需显式配置use_dictionary=true、columns、filters等参数才能优化;而pyarrow基于c++零拷贝实现,原生支持高效列裁剪、字典解码和复杂类型。

为什么用fastparquet读Parquet反而比pyarrow慢?
默认情况下,fastparquet不启用多线程解码,也不自动跳过未读列(column pruning),而pyarrow默认开启这些优化。尤其在宽表(上百列)或高基数字符串列上,fastparquet会把整行组(row group)全解压再过滤,造成明显延迟。
- 检查是否用了
engine="fastparquet"但没传use_dictionary=True(对重复字符串极关键) - 确认文件是否由
pyarrow写入且含dictionary_page——fastparquet读这类文件时若未显式启用字典解码,会退化为逐字节解析 -
fastparquet对INT96时间戳支持弱,遇到旧格式Parquet可能触发隐式类型转换和缓冲拷贝
必须设置的fastparquet核心参数
仅靠pd.read_parquet(..., engine="fastparquet")几乎无法发挥性能,以下参数缺一不可:
-
columns:显式指定只读列,强制列裁剪;不传则加载全部列,哪怕你后续只取df[["a","b"]] -
filters:传[("col", ">", 100)]类元组列表,让fastparquet跳过整个row group(需文件有统计信息) -
use_dictionary=True:对byte_array类型(如string)启用字典解码,减少内存分配和字符串对象创建 -
open_with:替换默认open为smart_open.open(若读S3/HDFS)或lru_cache(128)(open)(本地小文件频繁读)
示例:
pd.read_parquet(
"data.parq",
engine="fastparquet",
columns=["user_id", "event_time"],
filters=[("date", ">=", "2024-01-01")],
use_dictionary=True,
open_with=lru_cache(128)(open)
)
fastparquet与pyarrow的底层差异怎么影响速度?
fastparquet是纯Python实现(底层用numba加速部分循环),而pyarrow是C++核心+零拷贝内存映射。这意味着:
-
fastparquet无法绕过Python GIL,多进程读多个文件可提速,但单文件内并行度受限 -
pyarrow能直接复用Parquet页(page)的内存视图,fastparquet必须复制到numpy数组再转pandas,中间多一次内存拷贝 - 当文件含大量
DELTA_BINARY_PACKED整数编码时,fastparquet解码逻辑比pyarrow慢2–5倍(实测10GB整数列)
如果你的场景是“一次性读全量+简单清洗”,直接切pyarrow更省事;只有在嵌入式环境或需深度控制字典缓存策略时,才值得调优fastparquet。
哪些情况fastparquet根本没法优化?
不是所有慢都可调——以下情况换参数也没用:
- 文件由
spark.write.parquet()生成且未启用parquet.enable.dictionary=true(导致无字典页,use_dictionary=True失效) - 单个row group超256MB(
fastparquet加载时会全载入内存再切分,OOM风险高) - 列类型为
JSON或ARRAY嵌套结构——fastparquet解析嵌套性能远低于pyarrow,且不支持Arrow-native复杂类型
此时强行用fastparquet,不如用pyarrow配use_threads=True和memory_map=True,实际耗时更低、代码更稳。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











