如何在执行前更改查询计划(可能关闭优化)?

时间:2017-05-31 17:20:47

标签: apache-spark apache-spark-sql

我有一个简单的spark SQL查询:

SELECT x, y
FROM t1 INNER JOIN t2 ON t1.key = t2.key
WHERE expensiveFunction(t1.key)

其中expensiveFunction是一个spark UDF(用户定义的函数)。

当我查看spark生成的查询计划时,我发现它有两个过滤器操作而不是一个:它不仅会检查expensiveFunction(t1.key) ,还有expensiveFunction(t2.key)

image

通常,这种优化并不是一件坏事,因为它减少了要加入的记录数,而加入是一项昂贵的操作。但在我的情况下expensiveFunction(t2.key)总是返回true,所以我想删除它。

有没有办法在执行查询之前更改查询计划?有没有办法表明我不希望给定的优化应用于我的查询?

3 个答案:

答案 0 :(得分:1)

您可以像下面一样重写此查询以避免额外的函数调用。

SELECT x, y
FROM (SELECT <required-columns> FROM t1 WHERE expensiveFunction(t1.key)) t0 INNER JOIN t2 ON t0.key = t2.key

要确保您可以将此查询(SELECT FROM t1 WHERE expensiveFunction(t1.key))作为单独的DataFrame保留。然后将表t2与此DataFrame结合使用。

例如,假设我们分别为表df1df2提供了DataFrames t1t2。我们执行以下操作以避免expensiveFunction两次调用。

val df3 = df1.filter("col1 == 1") 
df3.persist() // forces evaluation of this dataframe and applies the expensive function filter on df1.
df3.createOrReplaceTempView("t1")
spark.sql("""SELECT t1.col1. t2.col2
FROM t1 INNER JOIN t2 ON t1.col2 = t2.col1""") // this query now have no reference to expensiveFunction

答案 1 :(得分:1)

  

有没有办法在执行查询之前更改查询计划?

总的来说,是的。 Spark SQL查询规划器和优化器中的扩展点很少,可以实现愿望

  

有没有办法表明我不想将给定的优化应用于我的查询?

除非优化允许,否则这几乎是不可能的。换句话说,您必须找出该规则是否可以选择将其关闭,例如具有CostBasedJoinReorderspark.sql.cbo.enabled配置属性的spark.sql.cbo.joinReorder.enabledeither is off CostBasedJoinReorder does nothing时)。

您可以编写一个自定义逻辑运算符,使优化无效(因为在给定未知逻辑运算符的情况下不会匹配),在优化阶段您将删除它。

使用extendedOperatorOptimizationRules注册自定义优化。

答案 2 :(得分:1)

发生这种情况是由于优化程序规则org.apache.spark.sql.catalyst.optimizer.InferFiltersFromConstraints 代码注释如下(github

  /**
   * Infers an additional set of constraints from a given set of equality constraints.
   * For e.g., if an operator has constraints of the form (`a = 5`, `a = b`), this returns an
   * additional constraint of the form `b = 5`.
   */
  def inferAdditionalConstraints(constraints: Set[Expression]): Set[Expression] 

您可以使用spark.sql.optimizer.excludedRules

禁用此优化程序规则
  

val OPTIMIZER_EXCLUDED_RULES = buildConf(“ spark.sql.optimizer.excludedRules”)      .doc(“配置要在优化器中禁用的规则列表,其中规则为+        “,以规则名称指定并以逗号分隔。不能保证所有的+        “此配置规则最终将被排除,因为某些规则是必需的” +        “为正确起见。优化程序将记录确实已排除的规则。”)      .stringConf      .createOptional   这样,过滤器将不会传播到两个连接的sid