spark sql join情况下谓词下推优化器PushPredicateThroughJoin

之前有总结过hive谓词下推优化:

从一个sql引发的hive谓词下推的全面复盘及源码分析(上)

从一个sql引发的hive谓词下推的全面复盘及源码分析(下)

spark sql谓词下推逻辑优化器PushDownPredicates包含了三个规则:

PushPredicateThroughJoin是sparksql中join(包括inner、left、right、full)情况的谓词下推的逻辑执行计划优化器

PushPredicateThroughJoin在处理Filter节点下为outerJoin情况时,会结合outerjoin消除优化器共同起作用Spark sql逻辑执行计划优化器——EliminateOuterJoin【消除outerjoin】

谓词可以下推的前提:不影响查询结果,要保证下推前和下推后两个sql执行得到的效果相同

代码流程

object PushPredicateThroughJoin extends Rule[LogicalPlan] with PredicateHelper {
  //split方法把condition分为三部分:左侧数据表的字段,右侧数据表的字段,不可以下推的字段(包括不确定性+不在左右表outputset里的字段)
  private def split(condition: Seq[Expression], left: LogicalPlan, right: LogicalPlan) = {
    val (pushDownCandidates, nonDeterministic) = condition.partition(_.deterministic)
    val (leftEvaluateCondition, rest) =
      pushDownCandidates.partition(_.references.subsetOf(left.outputSet))
    val (rightEvaluateCondition, commonCondition) =
        rest.partition(expr => expr.references.subsetOf(right.outputSet))
​
    (leftEvaluateCondition, rightEvaluateCondition, commonCondition ++ nonDeterministic)
  }
​
  def apply(plan: LogicalPlan): LogicalPlan = plan transform applyLocally
​
  val applyLocally: PartialFunction[LogicalPlan, LogicalPlan] = {
    // Filter+join的情况
    case f @ Filter(filterCondition, Join(left, right, joinType, joinCondition, hint)) =>
      val (leftFilterConditions, rightFilterConditions, commonFilterCondition) =
        split(splitConjunctivePredicates(filterCondition), left, right)
      joinType match {
        case _: InnerLike =>
          //Inner Join 把过滤条件下推到参加Join的两端
          val newLeft = leftFilterConditions.
            reduceLeftOption(And).map(Filter(_, left)).getOrElse(left)
          val newRight = rightFilterConditions.
            reduceLeftOption(And).map(Filter(_, right)).getOrElse(right)
          val (newJoinConditions, others) =
            commonFilterCondition.partition(canEvaluateWithinJoin)
          val newJoinCond = (newJoinConditions ++ joinCondition).reduceLeftOption(And)
​
          val join = Join(newLeft, newRight, joinType, newJoinCond, hint)
          if (others.nonEmpty) {
            Filter(others.reduceLeft(And), join)
          } else {
            join
          }
        case RightOuter =>
          // RightOuter,把where子句的右侧数据表的过滤条件下推到右侧数据表
          val newLeft = left
          val newRight = rightFilterConditions.
            reduceLeftOption(And).map(Filter(_, right)).getOrElse(right)
          val newJoinCond = joinCondition
          val newJoin = Join(newLeft, newRight, RightOuter, newJoinCond, hint)
​
          (leftFilterConditions ++ commonFilterCondition).
            reduceLeftOption(And).map(Filter(_, newJoin)).getOrElse(newJoin)
        case LeftOuter | LeftExistence(_) =>
          // LeftOuter,把where子句中左侧数据表的过滤条件下推到左侧数据表。
          val newLeft = leftFilterConditions.
            reduceLeftOption(And).map(Filter(_, left)).getOrElse(left)
          val newRight = right
          val newJoinCond = joinCondition
          val newJoin = Join(newLeft, newRight, joinType, newJoinCond, hint)
​
          (rightFilterConditions ++ commonFilterCondition).
            reduceLeftOption(And).map(Filter(_, newJoin)).getOrElse(newJoin)
        case FullOuter => f // FullOuter,不会下推where子句的过滤条件到数据表
        case NaturalJoin(_) => sys.error("Untransformed NaturalJoin node")
        case UsingJoin(_, _) => sys.error("Untransformed Using join node")
      }
​
    // Join+on的情况
    case j @ Join(left, right, joinType, joinCondition, hint) =>
      val (leftJoinConditions, rightJoinConditions, commonJoinCondition) =
        split(joinCondition.map(splitConjunctivePredicates).getOrElse(Nil), left, right)
​
      joinType match {
        case _: InnerLike | LeftSemi =>
          // Inner Join把on子句的过滤条件下推到参加Join的两端的数据中
          val newLeft = leftJoinConditions.
            reduceLeftOption(And).map(Filter(_, left)).getOrElse(left)
          val newRight = rightJoinConditions.
            reduceLeftOption(And).map(Filter(_, right)).getOrElse(right)
          val newJoinCond = commonJoinCondition.reduceLeftOption(And)
​
          Join(newLeft, newRight, joinType, newJoinCond, hint)
        case RightOuter =>
          // RightOuter,把on子句中左侧数据表的过滤条件下推到左侧数据表中
          val newLeft = leftJoinConditions.
            reduceLeftOption(And).map(Filter(_, left)).getOrElse(left)
          val newRight = right
          val newJoinCond = (rightJoinConditions ++ commonJoinCondition).reduceLeftOption(And)
​
          Join(newLeft, newRight, RightOuter, newJoinCond, hint)
        case LeftOuter | LeftAnti | ExistenceJoin(_) =>
          // LeftOuter,把on子句中右侧数据表的过滤条件下推到右侧数据表中。
          val newLeft = left
          val newRight = rightJoinConditions.
            reduceLeftOption(And).map(Filter(_, right)).getOrElse(right)
          val newJoinCond = (leftJoinConditions ++ commonJoinCondition).reduceLeftOption(And)
​
          Join(newLeft, newRight, joinType, newJoinCond, hint)
        case FullOuter => j //FullOuter,则不会下推on子句的过滤条件到数据表
        case NaturalJoin(_) => sys.error("Untransformed NaturalJoin node")
        case UsingJoin(_, _) => sys.error("Untransformed Using join node")
      }
  }
}

1、处理Filter节点下为Join(包括inner、left、right、full)节点的情况

1.1 inner join

Filter+inner join:把过滤条件下推到参加Join的两端

1.2 right join

Filter+right join,把where子句的右侧数据表的过滤条件下推到右侧数据表。在这个案例中因为满足【right outer join 且左表有过滤操作】这个条件,EliminateOuterJoin (outer join消除优化器) Spark sql逻辑执行计划优化器——EliminateOuterJoin【消除outerjoin】把right join 转成了 inner join ,因此,两侧都做了下推

1.3 left join

Filter+left join,把where子句的左侧数据表的过滤条件下推到左侧数据表

1.4 full join

Filter+full join,谓词下推优化器不会下推where子句的过滤条件到数据表, 在这个案例中因为满足【full join  且左表有过滤操作】这个条件,EliminateOuterJoin (outer join消除优化器) 把full join 转成了 left join ,因此Filter+full join —转化—>Filter+left join 。而PushPredicateThroughJoin对Filte+left join的形式做了下推。

2、处理Join节点中谓词在on里的情况

2.1 inner join

Inner Join+on,把on子句的过滤条件下推到参加Join的两端的数据中

2.2 right join

Right join+on,把on子句中左侧数据表的过滤条件下推到左侧数据表中

2.3 left join

left join+on,把on子句中右侧数据表的过滤条件下推到右侧数据表中

2.4 full join

full join+on,不能下推,不做任何操作

总结

EliminateOuterJoin+PushPredicateThroughJoin共同的效果

Hey!

我是小萝卜算子

每天学习一点点

知识增加一点点

思考深入一点点

在成为最厉害最厉害最厉害的道路上

很高兴认识你

推荐阅读:

从一个sql引发的hive谓词下推的全面复盘及源码分析(上)

从一个sql引发的hive谓词下推的全面复盘及源码分析(下)

Spark sql逻辑执行计划优化器——EliminateOuterJoin【消除outerjoin】

spark sql非join情况的谓词下推优化器PushPredicateThroughNonJoin

Spark sql Expression的deterministic属性

json_tuple一定比 get_json_object更高效吗?

with as 语句真的会把查询的数据存内存嘛?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
谓词下推是指在Hive中,尽量将过滤条件提前执行,使得最后参与join的表的数据量更小,从而减少数据传输IO,节约资源,提升性能。无论在Hive中是否开启了CBO(Cost-Based Optimizer),无论谓词写在ON后面还是WHERE后面,内连接(Inner Join)都会进行谓词下推。 在Hive中,谓词下推也称为Predicate Pushdown。它的实现方式是在map端提前执行过滤条件,减少map端的输出数据量。这样可以减少数据的传输和IO操作,提高查询性能。默认情况下,Hive会开启谓词下推,可以通过配置hive.optimize.ppd参数为true来开启或关闭谓词下推功能。 另外,在Hive中,如果在JOIN中有不能匹配上的表,则会使用null填充该表,这个表被称为Null Supplying Table。它是一种非保留表,用于提供null值。 综上所述,Hive SQL中的谓词下推是指在不影响结果的前提下,尽量将过滤条件提前执行,减少数据传输IO,节约资源,提升性能的优化技术。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [一文详解Hive谓词下推](https://blog.csdn.net/java_atguigu/article/details/123064220)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小萝卜算子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值