Pyspark 与谓词下推相关的数据块分区

Pyspark 与谓词下推相关的数据块分区,pyspark,azure-databricks,Pyspark,Azure Databricks,我已经寻找了很多简洁的答案,希望有人能帮助我弄清楚databricks分区 假设我有一个包含以下列的数据框:年,月,日,销售额,店号 我想按年份和月份存储此分区。。因此,我可以运行以下命令: df.write.partitionBy('Year', 'Month').format('csv').save('/mnt/path/', header='true') 这将以以下格式输出数据:/path/Year=2019/Month=05/.csv 如果我随后再次加载它,例如: spark.read

我已经寻找了很多简洁的答案,希望有人能帮助我弄清楚databricks分区

假设我有一个包含以下列的数据框:
销售额
店号

我想按年份和月份存储此分区。。因此,我可以运行以下命令:

df.write.partitionBy('Year', 'Month').format('csv').save('/mnt/path/', header='true')
这将以以下格式输出数据:
/path/Year=2019/Month=05/.csv

如果我随后再次加载它,例如:

spark.read.format('csv').options(header='true').load('/mnt/path/').createOrReplaceTempView("temp1")
Q1:这还没有真正“读取”数据,对吗?i、 我可能有几十亿张唱片。。但在我真正查询
temp1
之前,不会对源代码执行任何操作

Q2-A:随后,当使用
temp1
查询此数据时,我假设如果在where子句中包含分区中使用的项目,将对从磁盘读取的实际文件应用智能筛选

%sql
select * from temp1 where Year = 2019 and Month = 05 -- OPTIMAL
鉴于以下内容不会进行任何文件过滤,因为它没有要查找的分区的上下文:

%sql
select * from temp1 where StoreNum = 152 and SalesAmount > 10000 -- SUB-OPTIMAL
Q2-B:最后,如果我以拼花格式(而不是*.csv)存储文件。。上面的两个查询是否会“向下推”到实际存储的数据中。。但可能以不同的方式

也就是说,第一个仍然使用分区,但是第二个(
其中StoreNum=152和salesaumount>10000)现在将使用镶木地板的柱状存储?而*.csv没有这种优化

有谁能澄清我对此的想法/理解


链接到资源也很好。

Q1,当您在不提供模式的情况下读取csv文件时,它必须推断模式,并立即读取所有文件(如果可以的话,可能会在此时过滤分区)。 如果您要提供一个模式,那么您对过滤的假设和执行事件的假设都是正确的

问题2。我不太明白。当你说两个问题时,你的意思是高于还是低于?在下面的写操作中,您希望过滤如何在写操作中工作

如果您指的是parquet中的前两个查询,那么第一个查询将消除大多数文件,并且速度非常快。第二种方法希望通过使用文件中的统计信息来跳过一些数据,以表明它不需要读取它们。但它仍然会触及每个文件


您可能会发现这很有用

A1:您对
createOrReplaceTempView
的评估是正确的。这将在当前Spark会话中延迟评估。换句话说,如果您在不访问Spark会话的情况下终止该会话,则数据将永远不会传输到temp1

A2:让我们使用您的代码通过一个示例来检查这个案例。首先,让我们使用以下方法保存数据:

df.write.mode("overwrite").option("header", "true")
  .partitionBy("Year", "Month")
  .format("csv")
  .save("/tmp/partition_test1/")
然后加载以下内容:

val df1 = spark.read.option("header", "true")
                .csv("/tmp/partition_test1/")
                .where($"Year" === 2019 && $"Month" === 5)
执行
df1.explain
将返回:

== Physical Plan ==
*(1) FileScan csv [Day#328,SalesAmount#329,StoreNumber#330,Year#331,Month#332] Batched: false, Format: CSV, Location: InMemoryFileIndex[file:/tmp/partition_test1], PartitionCount: 0, Partition
Filters: [isnotnull(Year#331), isnotnull(Month#332), (Year#331 = 2019), (Month#332 = 5)], PushedFilters: [], ReadSchema: struct<Day:string,SalesAmount:string,StoreNumber:string>
现在,
PartitionFilters
PushedFilters
都将进行,以最大限度地减少Spark的工作量。正如您所见,Spark首先通过PartitionFilters识别现有分区,然后应用谓词下推来利用这两个筛选器

同样的情况也适用于
parquet
文件,其最大区别在于parquet将使用谓词下推过滤器,甚至将其与内部基于列的系统(如您所述)相结合,该系统将度量和统计信息保存在数据上。因此,与CSV文件的区别在于,在CSV的情况下,当Spark读取/扫描CSV文件时,会发生谓词下推,不包括不满足谓词下推条件的记录。对于拼花地板,谓词下推过滤器将传播到拼花地板内部系统,从而导致更大的数据修剪

在您的情况下,从
createOrReplaceTempView
加载数据不会有所不同,执行计划将保持不变

一些有用的链接:


谢谢你的回答,我又看了一遍问题,发现不清楚。。我现在(希望)通过将q2扩展到q2a和q2b来澄清。。。但本质上,我只是在寻找*.csv和*.parquet存储方式的验证,并在封面下进行“优化”。。。事实上,我昨天找到了那个链接,并仔细阅读了一遍,我会再看一遍的!回答得很好,谢谢你花时间!你的最后一点是:createOrReplaceTempView有没有一种方法可以通过拼花地板和csv来提高性能?您好@m1nkeh,我不这么认为。Spark SQL API和Dataframes API都将被解释为相同的逻辑/执行计划。SQL API适用于更喜欢编写SQL而不是数据帧语法的用户:)好的,那么如果保持数据帧语法,则会有优化?至少没有问题,我提到它是因为您已经在使用它了,而且因为您正在使用它,我想澄清一下,在执行计划中没有区别。在这个特定的情况下@m1nkeh没有什么需要改进的,因为它是非常简单的模式和查询:)但是在许多其他情况下,是的,您希望进行这样的改进
df1.where($"StoreNumber" === 1 && $"Year" === 2011 && $"Month" === 11).explain

== Physical Plan ==
*(1) Project [Day#462, SalesAmount#463, StoreNumber#464, Year#465, Month#466]
+- *(1) Filter (isnotnull(StoreNumber#464) && (cast(StoreNumber#464 as int) = 1))
   +- *(1) FileScan csv [Day#462,SalesAmount#463,StoreNumber#464,Year#465,Month#466] Batched: false, Format: CSV, Location: InMemoryFileIndex[file:/tmp/partition_test1], PartitionCount: 1, Par
titionFilters: [isnotnull(Month#466), isnotnull(Year#465), (Year#465 = 2011), (Month#466 = 11)], PushedFilters: [IsNotNull(StoreNumber)], ReadSchema: struct<Day:string,SalesAmount:string,Store
Number:string>