非常游戏网
如何加速Python读取Parquet文件的性能_使用PyArrow后端加载

如何加速Python读取Parquet文件的性能_使用PyArrow后端加载

2026-05-28日常编程21078

PyArrow直接读Parquet比pandas快,因绕过DataFrame转换、可指定列/过滤/禁用类型推断、复用内存池、优化文件布局及合理并行;关键在读写协同调优。

PyArrow读取Parquet时为什么比pandas慢?

默认用 pandas.read_parquet() 且后端是 pyarrow,其实底层调的仍是 pyarrow.parquet.read_table(),但多了一层DataFrame转换开销。真正提速的关键不是换库,而是绕过pandas、直接用 pyarrow.parquet.read_table() 控制读取粒度。

常见错误是只改后端不改接口:比如写 pandas.read_parquet("x.parq", engine="pyarrow") 却没指定 columnsfilters,结果全列全行加载,I/O和内存都浪费。

  • 只读需要的列:read_table("x.parq", columns=["user_id", "event_time"])
  • 跳过不匹配的row group:filters=[("status", "==", "success")](要求文件按该列排序或有统计信息)
  • 禁用自动类型推断(尤其字符串列):use_pandas_metadata=False,避免解析嵌套schema

如何复用PyArrow内存池减少GC压力

高频小批量读取(如ETL流水线)中,反复创建/销毁 pyarrow.Table 会触发Python GC,同时PyArrow内部也频繁分配内存。启用内存池可显著降低延迟波动。

PyArrow 12+ 默认使用系统内存池,但旧版本需显式设置;若用jemalloc或mimalloc,效果更明显。

  • 强制启用内存池:pa.default_memory_pool(pa.jemalloc_memory_pool())(需提前安装jemalloc)
  • 读取时绑定池:read_table(..., memory_pool=pa.default_memory_pool())
  • 注意:多线程下各线程应共享同一池,不要每个线程新建一个

Parquet文件本身结构对PyArrow读取速度的影响

PyArrow能否跳过row group、是否能向量化解码,高度依赖Parquet文件的物理布局。同一份数据,不同写法生成的文件,读取性能可能差3倍以上。

Python 3.14.3

微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。

关键控制点在写入侧(哪怕你只负责读):

  • row_group_size 推荐 64MB–128MB:太小则元数据膨胀、seek变多;太大则无法跳过无效数据
  • 启用字典编码(dictionary=True)对高基数字符串无效,反而拖慢;低基数(如状态码、分类标签)才收益明显
  • 按查询高频字段排序并写入(sort_by="timestamp"),让 filters 能利用min/max统计快速裁剪
  • 避免嵌套结构(struct/list/map)——PyArrow解嵌套开销大,且无法向量化

多文件并行读取时的线程/进程选择

单个Parquet文件内部是多row group并行可读的,PyArrow默认已启用多线程解码(通过 use_threads=True)。但多个文件一起读时,并行策略要小心。

ThreadPoolExecutor 在IO密集场景够用,但若文件在本地磁盘且数量多,GIL影响不大;若走网络存储(S3/HDFS),建议用 concurrent.futures.ProcessPoolExecutor 避免阻塞主线程。

  • 单机多文件 + 本地存储:ThreadPoolExecutor(max_workers=4) + 每个任务调 read_table(..., use_threads=True)
  • 单机多文件 + S3路径:ProcessPoolExecutor(max_workers=2),并在子进程中初始化 s3fs.S3FileSystem
  • 切忌:在主线程用 threading.Thread 手动管理,容易因未join或异常退出导致句柄泄漏

最易被忽略的是文件系统缓存——PyArrow不会自动缓存S3对象,如果反复读同一份文件,务必自己加LRU cache包装 read_table,否则每次都是网络往返。