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

为何Spark读取目录比读取文件路径列表快得多?

问题描述

我在S3中有一个包含数百万个小文件的目录,这些文件均小于10MB且为GZ格式,我知道这对Spark来说效率较低。我运行一个简单的批处理作业将这些文件转换为Parquet格式,尝试了两种读取方式:

第一种方式:

spark.read.csv("s3://input_bucket_name/data/")

第二种方式:

spark.read.csv("file1", "file2"... "file8million")

其中列表中的每个文件都位于同一存储桶和子文件夹中。

两种方式的表现差异:

  • 传入整个目录时,驱动程序的文件索引启动延迟较短(批处理开始前约20分钟),Spark UI中20分钟后仅显示一个转换任务。
  • 传入单个文件名列表时,索引时间增至2小时以上,转换作业在UI中要等这段时间后才会显示。文件列表模式下有两个任务:(1) 首先是列出800万个文件的叶子节点,(2) 之后是转换作业。

我想了解出现这种情况的原因,底层读取API是否存在差异导致该行为?


原因分析

这两种读取方式的底层处理逻辑存在显著差异,直接导致了索引时间的巨大差距:

  1. 目录模式:利用S3批量列表API高效处理
    当传入S3目录路径时,Spark依赖Hadoop S3A客户端调用S3的ListObjectsV2批量列表API:
  • 通过目录前缀一次性过滤出目标路径下的所有文件,支持分页批量返回结果,大幅减少API调用次数。
  • 批量获取所有文件的元数据(大小、修改时间等),网络往返开销被压缩到最低。
  • 整个列表与校验过程完成后直接进入数据转换阶段,因此Spark UI中只显示一个转换任务,前置索引耗时仅约20分钟。
  1. 单个文件列表模式:逐个校验的冗余开销
    当传入数百万个独立文件路径时,Spark会对每个路径执行独立操作:
  • 针对每个文件路径,Spark都会调用S3的HeadObjectAPI单独校验文件存在性、获取元数据。800万个文件意味着800万次独立API请求,网络开销呈数量级增长。
  • 驱动端需要逐个处理每个文件的校验结果,内存与CPU负载大幅提升,导致索引时间拉长至2小时以上。
  • Spark UI中显示的“列出800万个文件的叶子节点”任务,正是驱动端逐个校验文件路径的过程,只有完成这一步才会启动实际的转换任务,因此呈现两个阶段的任务流程。

额外说明:即使所有文件都在同一目录下,传入单个文件列表也无法复用S3的批量列表优化——Spark会将每个路径视为独立资源,而非同一目录下的集合,完全浪费了对象存储的特性优势。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 04:11:19