如何根据我拥有的其他数据框将过滤器下推到数据框读取?基本上想避免完全读取第二个数据帧然后进行内部连接。相反,我想在读取时提交一个过滤器来过滤源。即使我使用内部连接包裹读取,该计划也没有显示它被过滤。我觉得肯定有更好的方法来设置它。使用Spark 2.x到目前为止我有这个,但我想避免收集如下的List:
// Don't want to do this collect...too slow
val idFilter = df1.select("id").distinct().map(r => r.getLong(0)).collect.toList
val df2: DataFrame = spark.read.format("parquet").load("<path>")
.filter($"id".isin(idFilter: _*))
答案 0 :(得分:0)
除非您自己实现DataSource,否则不能直接使用谓词下推。谓词下推是Spark数据源提供的一种机制,必须由每个数据源单独实现。
对于基于文件的数据源,已经存在一种基于磁盘分区的简单机制。
考虑以下DataFrame:
val df = Seq(("test", "day1"), ("test2", "day2")).toDF("data", "day")
如果我们通过以下方式将该DataFrame保存到磁盘:
df.write.partitionBy("day").save("/tmp/data")
结果将是以下文件夹结构
tmp -
|
| - data - |
|
|--day=day1 -|- part1....parquet
| |- part2....parquet
|
|--day=day2 -|- part1....parquet
|- part2....parquet
如果您现在正像这样使用此数据源:
spark.read.load("/tmp/data").filter($"day" = "day1").show()
Spark甚至不必费心加载文件夹day2的数据,因为不需要它。
这是一种谓词下推式,适用于spark支持的每种标准文件格式。
更具体的机制是实木复合地板。 Parquet是基于列的文件格式,这意味着其退出很容易过滤出列。如果文件a
中有3列b
,c
,/tmp/myparquet.parquet
的基于镶木地板的文件,请执行以下查询:
spark.read.parquet("/tmp/myparquet.parquet").select("a").show()
将导致内部谓词下推,其中spark仅获取a
列的数据,而没有读取b
或c
列的数据。
如果有人对通过实现此特征而建立的机制感兴趣:
/**
* A BaseRelation that can eliminate unneeded columns and filter using selected
* predicates before producing an RDD containing all matching tuples as Row objects.
*
* The actual filter should be the conjunction of all `filters`,
* i.e. they should be "and" together.
*
* The pushed down filters are currently purely an optimization as they will all be evaluated
* again. This means it is safe to use them with methods that produce false positives such
* as filtering partitions based on a bloom filter.
*
* @since 1.3.0
*/
@Stable
trait PrunedFilteredScan {
def buildScan(requiredColumns: Array[String], filters: Array[Filter]): RDD[Row]
}
中找到