来自另一个表的分区列上的火花条件(性能)

时间:2019-07-13 21:16:17

标签: apache-spark apache-spark-sql

我在registration_ts列上分区了一个巨大的实木复合地板,名为stored

我想根据从小表-stream

获得的数据来过滤该表

在sql world中,查询如下:

spark.sql("select * from stored where exists (select 1 from stream where stream.registration_ts = stored.registration_ts)")

在Dataframe世界中:

stored.join(broadcast(stream), Seq("registration_ts"), "leftsemi")

这一切都可行,但是性能受到影响,因为未应用分区修剪。 Spark全扫描存储的表,这太昂贵了。

例如,此过程运行2分钟:

stream.count
res45: Long = 3
//takes 2 minutes
stored.join(broadcast(stream), Seq("registration_ts"), "leftsemi").collect
[Stage 181:>                                                      (0 + 1) / 373]

这将在3秒内运行:

val stream = stream.where("registration_ts in (20190516204l, 20190515143l,20190510125l, 20190503151l)")
stream.count
res44: Long = 42
//takes 3 seconds
stored.join(broadcast(stream), Seq("registration_ts"), "leftsemi").collect

原因是在第二个示例中,分区过滤器被传播到联接的stream表中。

我想对动态分区集进行分区过滤。

我能提出的唯一解决方案:

val partitions = stream.select('registration_ts).distinct.collect.map(_.getLong(0))
stored.where('registration_ts.isin(partitions:_*))

哪个将分区收集到驱动程序并进行第二次查询。这仅适用于少数分区。当我尝试使用具有500k个不同分区的解决方案时,延迟非常大。

但是必须有更好的方法...

1 个答案:

答案 0 :(得分:0)

这是在PySpark中完成此操作的一种方式,我已经在Zeppelin中进行了验证,它正在使用一组值来修剪分区

#collect_set函数返回一个不同的值列表,collect函数返回一个行列表。在行列表中获取[0]元素可获取第一行,而在行中[0]元素可获取第一列的值,第一列是不同值的列表
from pyspark.sql.functions import collect_set
filter_list = spark.read.orc(HDFS_PATH)
    .agg(collect_set(COLUMN_WITH_FILTER_VALUES))
    .collect()[0][0]
#您可以将filter_list与isin函数一起使用以修剪分区
df = spark.read.orc(HDFS_PATH)
    .filter(col(PARTITION_COLUMN)
    .isin(filter_list))
    .show(5)
#您可能需要对filter_list值进行一些检查,以确保在尝试执行下一个spark.read之前,您的第一个spark.read实际上返回了一个有效的值列表。