使用来自另一个数据帧的谓词下推过滤Dataframe

时间:2018-04-05 16:54:16

标签: apache-spark

如何根据我拥有的其他数据框将过滤器下推到数据框读取?基本上想避免完全读取第二个数据帧然后进行内部连接。相反,我想在读取时提交一个过滤器来过滤源。即使我使用内部连接包裹读取,该计划也没有显示它被过滤。我觉得肯定有更好的方法来设置它。使用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: _*))

1 个答案:

答案 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列bc/tmp/myparquet.parquet的基于镶木地板的文件,请执行以下查询:

spark.read.parquet("/tmp/myparquet.parquet").select("a").show()

将导致内部谓词下推,其中spark仅获取a列的数据,而没有读取bc列的数据。

如果有人对通过实现此特征而建立的机制感兴趣:

/**
 * 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]
}

可在https://github.com/apache/spark/blob/master/sql/core/src/main/scala/org/apache/spark/sql/sources/interfaces.scala

中找到