当联接密钥是bucketBy密钥的超集时,如何说服火花不要进行交换?

时间:2019-06-25 23:53:05

标签: apache-spark join bucket hive-metastore

在测试生产用例时,我已经创建并保存(使用Hive Metastore)这样的表:

table1:
fields: key1, key2, value1
sortedBy key1,key2
bucketBy: key1, 100 buckets

table2:
fields: key1, key2, value2
sortedBy: key1,key2
bucketBy: key1, 100 buckets

我正在运行这样的查询(使用伪代码)

table1.join(table2, [“key1”, “key2”])
 .groupBy(“value2”)
 .countUnique(“key1”)

常识说,这种连接应该简单地通过没有任何交换的排序合并连接来完成;但是spark会进行交流然后加入。

尽管对于这个特定用例,由于我需要按key1进行存储的其他一些用例,我还是可以对两个键进行存储。而当我使用这样的单个键进行(简单)连接时:

table1.join(table2, [“key1”])

它按预期工作(即不进行任何交换的排序合并联接)。

现在,如果要过滤,我在这些表上有一个优化的联接:

table1.join(table2, [“key1”])
 .filter(table1.col(“key2”) == table2.col(“key2”))

它恢复为交换,然后加入。

当连接键是bucketBy键的超集时,如何说服火花不要进行交换?

4 个答案:

答案 0 :(得分:1)

基于一些研究和探索,这似乎是最不容易破解的解决方案:

基于此示例:

table1.join(table2, [“key1”])
      .filter(table1.col(“key2”) == table2.col(“key2”))

代替使用Spark中的equalTo (==),实现自定义MyEqualTo(通过委托spark EqualTo实现很好)似乎可以解决此问题。这样,spark不会优化(!)联接,它只会将过滤器拉到SortMergeJoin中。

类似地,联接条件也可以这样形成:

(table1.col(“key1”) == table2.col(“key1”)) AND
table1.col(“key2”).myEqualTo(table2.col(“key2”))

答案 1 :(得分:0)

org.apache.spark.sql.catalyst.optimizer.PushPredicateThroughJoin是优化程序规则,用于通过谓词推送谓词。 ~~
我们可以从优化程序规则中排除该规则。这样,我们无需对用户代码进行任何更改。
 要排除的情况,我们可以执行以下操作之一
1. --conf spark.sql.optimizer.excludedRules=org.apache.spark.sql.catalyst.optimizer.PushPredicateThroughJoin
2.将属性添加到spark-defaults .conf。
3.将set spark.sql.optimizer.excludedRules=org.apache.spark.sql.catalyst.optimizer.PushPredicateThroughJoin添加到用户代码中。

再次,这再次是黑客。
理想情况下,应该通过连接将过滤器下推,这样可以减少要连接的行数

更新 :。
1.关于下推我是错的。由于谓词具有两个表中的列,因此将没有过滤器下推
2. 为什么当 where 子句具有“非相等”谓词时,SortMergeJoin(SMJ)为什么不添加其他交换?
这是因为SMJ只能将基于相等性的谓词视为连接条件org.apache.spark.sql.catalyst.planning.ExtractEquiJoinKeys#unapply

的一部分

负责添加交换的确保需求看到SMJ没有新的加入条件,并且输出分配已经满足。
代码:org.apache.spark.sql.execution.exchange.EnsureRequirements#ensureDistributionAndOrdering
3. 哪种方法有效?添加一个等于或大于等于谓词的谓词的UDF?
为了对此进行评估,我使用来检查生成的代码,

val df = spark.sql(<joinquery>)
df.queryExecution.debug.codegen

a。 UDF等于-涉及虚拟函数调用的额外开销。
b。小于和大于的组合-没有虚拟函数调用。一旦找到连接的行(使用key1),代码就会一个接一个地检查其他谓词。

根据上述3中的观察,使用基于非等式的谓词似乎更有效。

答案 2 :(得分:0)

**根据您的伪代码**

table1.join(table2,[“ key1”,“ key2”])  .groupBy(“ value2”)  .countUnique(“ key1”)

我想解决办法是

  

第一步,只需连接表并获取数据框。

df = table1.join(table2, [“key1”, “key2”])
  

然后分组并进行不同的计数

df.select(“value2”,“key1”).distinct().groupBy(“value2”,“key1”).count().show()

答案 3 :(得分:0)

我正面临着同样的问题。 看来PR已完成,正好解决了这个问题

(PR)https://github.com/apache/spark/pull/19054

(吉拉票)https://issues.apache.org/jira/browse/SPARK-18067

但是我希望它已经被包含在内(我正在使用Spark 3.0.0,并且问题仍然存在,而票证已于2019年5月21日解决,距离Spark3发布已经一年多了。) >

感谢使用不等式运算符的“ hack”,虽然感觉不太好,但这是一个简单的解决方法。我还将尝试使用PR中的解决方案来修补我的spark版本,但是如果我要共享我的代码,这将不太可持续/不可再现。