在读取Spark中按列spark.read.json("/A=1/B=2/C=3/D=4/E=5/")
进行分区的数据时,将仅扫描文件夹E = 5中的文件。
但是,假设我有兴趣通过所有数据源读取其中C = my_value
的分区。指令将为spark.read.json("/*/*/C=my_value/")
。
在引擎盖下描述的场景中,计算发生了什么? Spark只会列出A和B的分区值吗?还是也会扫描所有叶子(实际文件)?
答案 0 :(得分:2)
感谢您提出一个有趣的问题。 Apache Spark使用Hadoop的FileSystem
抽象来处理通配符模式。在源代码中,它们被称为全局模式
org.apache.hadoop.fs.FileSystem#globStatus(org.apache.hadoop.fs.Path)
方法用于返回“与路径模式匹配的路径数组”。然后,此函数调用org.apache.hadoop.fs.Globber#glob
来为glob模式找出确切的文件匹配算法。 org.apache.spark.sql.execution.datasources.DataSource#checkAndGlobPathIfNecessary
调用globStatus。您可以添加一些断点以了解其在后台的工作方式。
但长话短说:
在引擎盖下描述的场景中,计算发生了什么? Spark只会列出A和B的分区值吗?还是也会扫描所有叶子(实际文件)?
Spark会将您的glob分成3个部分[“ *”,“ *”,“ C = my_value”]。稍后,它将使用Hadoop org.apache.hadoop.fs.FileSystem#listStatus(org.apache.hadoop.fs.Path)
方法列出每个级别的文件。对于每个文件,它将建立一个路径,并尝试将其与当前模式匹配。当算法寻找“ C = my_value”时,匹配文件将保留为“候选”,仅在最后一步被过滤掉。
除非您有很多文件,否则此操作不会对您造成伤害。也许这就是为什么您宁愿保留较少但较大的文件的原因之一(著名的数据工程问题是“过多的小文件”)。