transformation算子基本原理五

前言

        本篇文章从源码角度分析下join、leftouterjoin、rightouterjoin、fullouterjoin、intersection五个算子的基本原理,他们底层都是依赖cogroup算子,关于cogroup算子的原理请参考。下面分别分析以上五个算子的源码

join

  def join[W](other: RDD[(K, W)], partitioner: Partitioner): RDD[(K, (V, W))] = self.withScope {
    this.cogroup(other, partitioner).flatMapValues( pair =>
      for (v <- pair._1.iterator; w <- pair._2.iterator) yield (v, w)
    )
  }

join算子实现的功能就是hive sql中的inner join
首先两个RDD做一个cogroup运算,得到一个新的RDD(CoGroupedRDD),其元素类型为(key, (iterable, iterable))(具体解释请参考)
对新生成的CoGroupedRDD做flatMapValues(原理请参考transformation算子基本原理二)运算。CoGroupedRDD的value类型为(iterable, iterable),遍历两个iterable(for (v <- pair._1.iterator; w <- pair._2.iterator)),即做笛卡尔积,两个迭代器都不为空则将每一对元素装入一个集合(yield (v, w)),最后对返回的集合做flat操作。注意join算子的返回类型是RDD[(K, (V, W))],注意value的类型(V, W),V和W是不同的类型,表示原始两个RDD中元素(k,v)的v类型是可以不同的

leftoutjoin

  def leftOuterJoin[W](
      other: RDD[(K, W)],
      partitioner: Partitioner): RDD[(K, (V, Option[W]))] = self.withScope {
    this.cogroup(other, partitioner).flatMapValues { pair =>
      // 先判断右迭代器是否为空
      if (pair._2.isEmpty) {
        pair._1.iterator.map(v => (v, None))
      } else {
        for (v <- pair._1.iterator; w <- pair._2.iterator) yield (v, Some(w))
      }
    }
  }

leftoutjoin算子原理和join算子原理一样,只是比join多了一个判断:
if (pair._2.isEmpty) {pair._1.iterator.map(v => (v, None)) }。大家知道对于sql中的左连接,不管右表是否有记录,左表的记录都保留,右表字段都为null。所以对于leftoutjoin,先要判断右边的迭代器是否为空,如果为空,直接返回左边迭代器的元素

rightoutjoin

  def rightOuterJoin[W](other: RDD[(K, W)], partitioner: Partitioner)
      : RDD[(K, (Option[V], W))] = self.withScope {
    this.cogroup(other, partitioner).flatMapValues { pair =>
      // 先判断左迭代器是否为空
      if (pair._1.isEmpty) {
        pair._2.iterator.map(w => (None, w))
      } else {
        for (v <- pair._1.iterator; w <- pair._2.iterator) yield (Some(v), w)
      }
    }
  }

rightoutjoin算子原理和join算子原理一样,只是比join多了一个判断:
if (pair._2.isEmpty) {pair._1.iterator.map(v => (v, None)) }。大家知道对于sql中的右连接,不管左表是否有记录,右表的记录都保留,左表字段都为null。所以对于rightoutjoin,先要判断左边的迭代器是否为空,如果为空,直接返回右边迭代器的元素

fullouterjoin

  def fullOuterJoin[W](other: RDD[(K, W)], partitioner: Partitioner)
      : RDD[(K, (Option[V], Option[W]))] = self.withScope {
    this.cogroup(other, partitioner).flatMapValues {
      case (vs, Seq()) => vs.iterator.map(v => (Some(v), None))
      case (Seq(), ws) => ws.iterator.map(w => (None, Some(w)))
      case (vs, ws) => for (v <- vs.iterator; w <- ws.iterator) yield (Some(v), Some(w))
    }
  }

sql中,full outer join 左右表的记录都需要保留,fullouterjoin的算子逻辑也是此原理:
case (vs, Seq()) => vs.iterator.map(v => (Some(v), None)):如果右迭代器为空,直接返回左迭代器元素;
case (Seq(), ws) => ws.iterator.map(w => (None, Some(w))):如果左迭代器为空,直接返回右迭代器元素;
case (vs, ws) => for (v <- vs.iterator; w <- ws.iterator) yield (Some(v), Some(w)):如果都不为空,返回左右迭代器元素的笛卡尔积

intersection

  def intersection(other: RDD[T]): RDD[T] = withScope {
    this.map(v => (v, null)).cogroup(other.map(v => (v, null)))
        .filter { case (_, (leftGroup, rightGroup)) => leftGroup.nonEmpty && rightGroup.nonEmpty }
        .keys
  }

和上述四个算子属于PairRDDFunctions类不同的是,intersection在RDD类中。其作用就是两个RDD做交集。

  • 首先将两个RDD做一个转化,v => (v, null), 转化为(k,v)类型的RDD
  • 然后对上述两个新RDD做一个cogroup计算,得到一个CoGroupedRDD
  • 然后对CoGroupedRDD中的元素进行过滤,左右迭代器同时不为空(注意,不是迭代器中的元素不为空,是迭代器中是否有元素。迭代器中的元素都是null元素)为真,和join算子的原理差不多
  • 最后对返回的RDD做一个keys的操作,因为返回的元素类型是(_, (leftGroup, rightGroup)),但是我们只需要key即可

大家是不是感觉intersection和join原理差不多,但是他们有两个不同点:

  • intersection可以作用在任何类型的RDD上,而join是作用在(k,v)类型的RDD上
  • 参与intersection计算的两个RDD的元素类型必须一样,join算子的v类型可以不一致
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值