我有一个简单的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)
。
通常,这种优化并不是一件坏事,因为它减少了要加入的记录数,而加入是一项昂贵的操作。但在我的情况下expensiveFunction(t2.key)
总是返回true,所以我想删除它。
有没有办法在执行查询之前更改查询计划?有没有办法表明我不希望给定的优化应用于我的查询?
答案 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结合使用。
例如,假设我们分别为表df1
和df2
提供了DataFrames t1
和t2
。我们执行以下操作以避免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查询规划器和优化器中的扩展点很少,可以实现愿望
有没有办法表明我不想将给定的优化应用于我的查询?
除非优化允许,否则这几乎是不可能的。换句话说,您必须找出该规则是否可以选择将其关闭,例如具有CostBasedJoinReorder
或spark.sql.cbo.enabled
配置属性的spark.sql.cbo.joinReorder.enabled
(either 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