我在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个不同分区的解决方案时,延迟非常大。
但是必须有更好的方法...
答案 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实际上返回了一个有效的值列表。