
[英]What is the difference between createOrReplaceTempView and pyspark dataframe
[英]What is DataFilter in pyspark?
我在查询执行计划中看到名为DataFilter
的东西:
FileScan parquet [product_id#12,price#14] Batched: true, DataFilters: [isnotnull(product_id#12)], Format: Parquet, Location: InMemoryFileIndex[gs://monsoon-credittech.appspot.com/spark_datasets/products_parquet_dtc], PartitionFilters: [], PushedFilters: [IsNotNull(product_id)], ReadSchema: struct<product_id:int,price:int>
有一个
我了解PartitionFilter
和PushedFilter
。 但是,这里显示的DataFilter
是什么? 这里有一个类似问题的答案。 但是,给出的DataFilter
的定义正是我认为的PushedFilter
是什么(此外,该答案有 1 个反对票)。 那么,我对PushedFilter
的理解是错误的吗? 如果不是,那么DataFilter
是什么?
此说明适用于本文发布时 Spark 的最新版本 (3.3.1)。
PushedFilters
是DataFilters
的一个子集,您可以在DataSourceScanExec.scala
中看到这一点。 它们是DataFilters
,我们可以下推其谓词以过滤您尝试读入的文件的元数据,而不是针对数据本身。 对元数据进行过滤当然比对数据本身进行过滤要快得多,因为这样做时您可以跳过读取大块数据。
因此,为了构造一切,我们有:
DataFilters
DataFilter
而不是PushedFilter
时,这意味着我们无法下推谓词来过滤基础文件的元数据。让我们以镶木地板文件为例(并非所有文件格式都支持谓词下推,但镶木地板文件支持):
import org.apache.spark.sql.functions.col
val df = Seq(
(1,2,3),
(2,2,3),
(3,20,300),
(1,24,299),
).toDF("colA", "colB", "colC")
df.write.partitionBy("colA").mode("overwrite").parquet("datafilter.parquet")
所以我们只是在编写一个由colA
列分区的镶木地板文件。 文件结构如下所示:
datafilter.parquet/
├── colA=1
│ ├── part-00000-55cb3320-f145-4d64-8cba-55a72111c0c8.c000.snappy.parquet
│ └── part-00003-55cb3320-f145-4d64-8cba-55a72111c0c8.c000.snappy.parquet
├── colA=2
│ └── part-00001-55cb3320-f145-4d64-8cba-55a72111c0c8.c000.snappy.parquet
├── colA=3
│ └── part-00002-55cb3320-f145-4d64-8cba-55a72111c0c8.c000.snappy.parquet
└── _SUCCESS
让我们看一下 3 种过滤器类型:
spark.read.parquet("./datafilter.parquet").filter(col("colA") < 10).explain
== Physical Plan ==
*(1) ColumnarToRow
+- FileScan parquet [colB#165,colC#166,colA#167] Batched: true, DataFilters: [], Format: Parquet, Location: InMemoryFileIndex(1 paths)[file:somePath/spark-tests/datafilter.parquet], PartitionFilters: [isnotnull(colA#167), (colA#167 < 10)], PushedFilters: [], ReadSchema: struct<colB:int,colC:int>
在这里你看到我们的过滤器是一个PartitionFilter
,因为我们的数据已经被colA
分区,我们可以很容易地过滤目录。
spark.read.parquet("./datafilter.parquet").filter(col("colB") < 10).explain
== Physical Plan ==
*(1) Filter (isnotnull(colB#172) AND (colB#172 < 10))
+- *(1) ColumnarToRow
+- FileScan parquet [colB#172,colC#173,colA#174] Batched: true, DataFilters: [isnotnull(colB#172), (colB#172 < 10)], Format: Parquet, Location: InMemoryFileIndex(1 paths)[file:somePath/spark-tests/datafilter.parquet], PartitionFilters: [], PushedFilters: [IsNotNull(colB), LessThan(colB,10)], ReadSchema: struct<colB:int,colC:int>
在这里您可以看到我们的过滤器 ( colB < 10
) 构成了DataFilters
的一部分。 这是因为colB
不是分区列。
它也是PushedFilters
的一部分,因为这是我们可以下推的谓词。 Parquet 文件将块的最小值和最大值存储为元数据。 所以如果一个块的最小值大于 10,我们知道我们可以跳过读取这个块。
spark.read.parquet("./datafilter.parquet").filter(col("colB") < col("colC")).explain
== Physical Plan ==
*(1) Filter ((isnotnull(colB#179) AND isnotnull(colC#180)) AND (colB#179 < colC#180))
+- *(1) ColumnarToRow
+- FileScan parquet [colB#179,colC#180,colA#181] Batched: true, DataFilters: [isnotnull(colB#179), isnotnull(colC#180), (colB#179 < colC#180)], Format: Parquet, Location: InMemoryFileIndex(1 paths)[file:somePath/spark-tests/datafilter.parquet], PartitionFilters: [], PushedFilters: [IsNotNull(colB), IsNotNull(colC)], ReadSchema: struct<colB:int,colC:int>
这个过滤器比较复杂。 colB < colC
不是我们可以下推以过滤到 parquet 文件的元数据的过滤器。 这意味着我们需要在 memory 中读取完整数据并进行过滤。
问题未解决?试试以下方法:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.