1.规律

   如果JoinAPI之前被调用的RDD API是宽依赖(存在shuffle), 而且两个join的RDD的分区数量一致,join结果的rdd分区数量也一样,这个时候join api是窄依赖
  除此之外的,rdd 的join api是宽依赖

2.Join的理解

  

3.举例

 A表数据:
1 a
2 b
3 c
B表数据:
1 aa1
1 aa2
2 bb1
2 bb2
2 bb3
4 dd1 A inner join B:
1 a 1 aa1
1 a 1 aa2
2   b 2 bb1
2   b 2 bb2
2   b 2 bb3 A left outer join B:
1 a 1 aa1
1 a 1 aa2
2   b 2 bb1
2   b 2 bb2
2   b 2 bb3
3   c null null A right outer join B:
1 a 1 aa1
1 a 1 aa2
2   b 2 bb1
2   b 2 bb2
2   b 2 bb3
null null 4 dd1 A full outer join B:
1 a 1 aa1
1 a 1 aa2
2   b 2 bb1
2   b 2 bb2
2   b 2 bb3
3   c null null
null null 4 dd1 A left semi join B:
1 a
2 b

4.API

  必须是Key/value键值对

  

5.测试程序

 import org.apache.spark.{SparkConf, SparkContext}

 /**
* RDD数据Join相关API讲解
* Created by ibf on 02/09.
*/
object RDDJoin {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setMaster("local[*]")
.setAppName("RDD-Join")
val sc = SparkContext.getOrCreate(conf) // ==================具体代码======================
// 模拟数据产生
val rdd1 = sc.parallelize(Array(
(1, "张三1"),
(1, "张三2"),
(2, "李四"),
(3, "王五"),
(4, "Tom"),
(5, "Gerry"),
(6, "莉莉")
), 1) val rdd2 = sc.parallelize(Array(
(1, "上海"),
(2, "北京1"),
(2, "北京2"),
(3, "南京"),
(4, "纽约"),
(6, "深圳"),
(7, "香港")
), 1) // 调用RDD API实现内连接
val joinResultRDD = rdd1.join(rdd2).map {
case (id, (name, address)) => {
(id, name, address)
}
}
println("----------------")
joinResultRDD.foreachPartition(iter => {
iter.foreach(println)
})
// 调用RDD API实现左外连接
val leftJoinResultRDd = rdd1.leftOuterJoin(rdd2).map {
case (id, (name, addressOption)) => {
(id, name, addressOption.getOrElse("NULL"))
}
}
println("----------------")
leftJoinResultRDd.foreachPartition(iter => {
iter.foreach(println)
})
// 左外连接稍微变化一下:需要左表出现,右表不出现的数据(not in)
println("----------------")
rdd1.leftOuterJoin(rdd2).filter(_._2._2.isEmpty).map {
case (id, (name, _)) => (id, name)
}.foreachPartition(iter => {
iter.foreach(println)
}) // 右外连接
println("----------------")
rdd1
.rightOuterJoin(rdd2)
.map {
case (id, (nameOption, address)) => {
(id, nameOption.getOrElse("NULL"), address)
}
}
.foreachPartition(iter => iter.foreach(println)) // 全外连接
println("----------------")
rdd1
.fullOuterJoin(rdd2)
.map {
case (id, (nameOption, addressOption)) => {
(id, nameOption.getOrElse("NULL"), addressOption.getOrElse("NULL"))
}
}
.foreachPartition(iter => iter.foreach(println)) // 休眠为了看4040页面
Thread.sleep(1000000)
}
}

6.说明 

 RDD join API:
  def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]
    返回值是RDD,RDD中的类型是一个二元组(a),a第一个元素是KEY类型的值(join的key), a第二个元素又是二元组(b), b的第一个元素是来自调用join函数的RDD的value,
    b的第二个元素是来自参数other这个RDD的value

  def leftOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (V, Option[W]))]
    对于右边的数据返回的是Option类型是数据,所以如果右表数据不存在,返回的是None;否则是一个Some的具体数据

  def rightOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (Option[V], W))]
    对于左边的数据返回的是Option类型是数据,所以如果左表数据不存在,返回的是None;否则是一个Some的具体数据

  def fullOuterJoin[W](other: RDD[(K, W)]): RDD[(K, (Option[V], Option[W]))]
    返回的value类型是Option封装后的数据,如果数据不存在, 返回的是None,存在返回的是Some具体数据

7.缺点

  

8.优化程序

  没有使用API,根据原理写一个。

  减少shufflw算子的使用。

 import org.apache.spark.{SparkConf, SparkContext}

 /**
* RDD数据Join相关API讲解
* Created by ibf on 02/09.
*/
object RDDJoin {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setMaster("local[*]")
.setAppName("RDD-Join")
val sc = SparkContext.getOrCreate(conf) // ==================具体代码======================
// 模拟数据产生
val rdd1 = sc.parallelize(Array(
(1, "张三1"),
(1, "张三2"),
(2, "李四"),
(3, "王五"),
(4, "Tom"),
(5, "Gerry"),
(6, "莉莉")
), 1) val rdd2 = sc.parallelize(Array(
(1, "上海"),
(2, "北京1"),
(2, "北京2"),
(3, "南京"),
(4, "纽约"),
(6, "深圳"),
(7, "香港")
), 1) // 假设rdd2的数据比较少,将rdd2的数据广播出去
val leastRDDCollection = rdd2.collect()
val broadcastRDDCollection = sc.broadcast(leastRDDCollection) println("++++++++++++++++++")
// 类似Inner Join的操作,Inner Join的功能:将两个表都出现的数据合并
println("-------------------")
rdd1
// 过滤rdd1中的数据,只要在rdd1中出现的数据,没有出现的数据过滤掉
.filter(tuple => broadcastRDDCollection.value.map(_._1).contains(tuple._1))
// 数据合并,由于一条rdd1的数据可能在rdd2中存在多条对应数据,所以使用flatMap
.flatMap {
case (id, name) => {
broadcastRDDCollection.value.filter(_._1 == id).map {
case (_, address) => {
(id, name, address)
}
}
}
}
.foreachPartition(iter => iter.foreach(println)) // 左外连接
println("---------------------")
rdd1
.flatMap {
case (id, name) => {
// 从右表所属的广播变量中获取对应id的集合列表
val list = broadcastRDDCollection.value.filter(_._1 == id)
// 对应id的集合可能为空,也可能数据有多个
if (list.nonEmpty) {
// 存在多个
list.map(tuple => (id, name, tuple._2))
} else {
// id在右表中不存在,填默认值
(id, name, "NULL") :: Nil
}
}
}
.foreachPartition(iter => iter.foreach(println)) // 右外连接
/**
* rdd2中所有数据出现,由于rdd2中的数据在driver中可以存储,可以认为rdd1和rdd2通过right join之后的数据也可以在driver中保存下
**/
println("---------------------")
// 将rdd1中符合条件的数据过滤出来保存到driver中
val stage1 = rdd1
.filter(tuple => broadcastRDDCollection.value.map(_._1).contains(tuple._1))
.collect()
// 将driver中两个集合进行right join
val stage2 = leastRDDCollection.flatMap {
case (id, address) => {
val list = stage1.filter(_._1 == id)
if (list.nonEmpty) {
list.map(tuple => (id, tuple._2, address))
} else {
Iterator.single((id, "NULL", address))
}
}
}
stage2.foreach(println) // TODO: 全外连接,不写代码,因为代码比较复杂 //====================================
// 左半连接:只出现左表数据(要求数据必须在右表中也出现过),如果左表的数据在右表中出现多次,最终结果只出现一次
println("+++++++++++++++++")
println("-----------------------")
rdd1
.join(rdd2)
.map {
case (id, (name, _)) => (id, name)
}
.distinct()
.foreachPartition(iter => iter.foreach(println))
println("------------------------")
rdd1
.filter(tuple => broadcastRDDCollection.value.map(_._1).contains(tuple._1))
.foreachPartition(iter => iter.foreach(println)) // 休眠为了看4040页面
Thread.sleep(1000000)
}
}

9.Join的窄依赖程序

  使用reduceByKey,里面的程序会给一个分区。

 package com.ibeifeng.senior.join

 import org.apache.spark.{SparkConf, SparkContext}

 /**
* RDD数据Join相关API讲解
* Created by ibf on 02/09.
*/
object RDDJoin2 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setMaster("local[*]")
.setAppName("RDD-Join")
val sc = SparkContext.getOrCreate(conf) // ==================具体代码======================
// 模拟数据产生, 添加map、reduceByKey、mapPartitions等api的主要功能是给rdd1和rdd2中添加一个分区器(表示当前rdd是存在shuffle过程的)
val rdd1 = sc.parallelize(Array(
(1, "张三1"),
(1, "张三2"),
(2, "李四"),
(3, "王五"),
(4, "Tom"),
(5, "Gerry"),
(6, "莉莉")
), 1).map(x => (x, null)).reduceByKey((x,y) => x, 1).mapPartitions(
iter => iter.map(tuple => tuple._1),
true // 使用上一个RDD的分区器,false表示不使用, 设置为None
) val rdd2 = sc.parallelize(Array(
(1, "上海"),
(2, "北京1"),
(2, "北京2"),
(3, "南京"),
(4, "纽约"),
(6, "深圳"),
(7, "香港")
), 1).map(x => (x, null)).reduceByKey((x,y) => x, 1).mapPartitions(
iter => iter.map(tuple => tuple._1),
true // 使用上一个RDD的分区器,false表示不使用, 设置为None
) // 调用RDD API实现内连接
val joinResultRDD = rdd1.join(rdd2).map {
case (id, (name, address)) => {
(id, name, address)
}
}
println("----------------")
joinResultRDD.foreachPartition(iter => {
iter.foreach(println)
}) // 休眠为了看4040页面
Thread.sleep(1000000)
}
}

030 RDD Join中宽依赖与窄依赖的判断的更多相关文章

  1. Spark 中的宽依赖和窄依赖

    Spark中RDD的高效与DAG图有着莫大的关系,在DAG调度中需要对计算过程划分stage,而划分依据就是RDD之间的依赖关系.针对不同的转换函数,RDD之间的依赖关系分类窄依赖(narrow de ...

  2. 大数据开发-从cogroup的实现来看join是宽依赖还是窄依赖

    前面一篇文章提到大数据开发-Spark Join原理详解,本文从源码角度来看cogroup 的join实现 1.分析下面的代码 import org.apache.spark.rdd.RDD impo ...

  3. Spark --【宽依赖和窄依赖】

    前言 Spark中RDD的高效与DAG图有着莫大的关系,在DAG调度中需要对计算过程划分stage,暴力的理解就是stage的划分是按照有没有涉及到shuffle来划分的,没涉及的shuffle的都划 ...

  4. Spark宽依赖、窄依赖

    在Spark中,RDD(弹性分布式数据集)存在依赖关系,宽依赖和窄依赖. 宽依赖和窄依赖的区别是RDD之间是否存在shuffle操作. 窄依赖 窄依赖指父RDD的每一个分区最多被一个子RDD的分区所用 ...

  5. Spark剖析-宽依赖与窄依赖、基于yarn的两种提交模式、sparkcontext原理剖析

    Spark剖析-宽依赖与窄依赖.基于yarn的两种提交模式.sparkcontext原理剖析 一.宽依赖与窄依赖 二.基于yarn的两种提交模式深度剖析 2.1 Standalne-client 2. ...

  6. Spark RDD概念学习系列之RDD的依赖关系(宽依赖和窄依赖)(三)

    RDD的依赖关系?   RDD和它依赖的parent RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency). 1)窄依赖指的是每 ...

  7. 小记--------spark的宽依赖与窄依赖分析

    窄依赖: Narrow Dependency : 一个RDD对它的父RDD,只有简单的一对一的依赖关系.RDD的每个partition仅仅依赖于父RDD中的一个partition,父RDD和子RDD的 ...

  8. spark-宽依赖和窄依赖

    一.窄依赖(Narrow Dependency,) 即一个RDD,对它的父RDD,只有简单的一对一的依赖关系.也就是说, RDD的每个partition ,仅仅依赖于父RDD中的一个partition ...

  9. spark 划分stage Wide vs Narrow Dependencies 窄依赖 宽依赖 解析 作业 job stage 阶段 RDD有向无环图拆分 任务 Task 网络传输和计算开销 任务集 taskset

    每个job被划分为多个stage.划分stage的一个主要依据是当前计算因子的输入是否是确定的,如果是则将其分在同一个stage,从而避免多个stage之间的消息传递开销. http://spark. ...

随机推荐

  1. CF888G Xor-MST 解题报告

    CF888G Xor-MST 题意翻译 给定一个\(n\)个节点的完全图,每个节点有个编号\(a_i\),节点\(i\)和节点\(j\)之间边的权值为\(a_i\ xor\ a_j\),求该图的最小生 ...

  2. django 学习笔记(转)

    原文链接:https://my.oschina.net/linktime/blog/105280 例如有一下模型 from django.db import models class person(m ...

  3. 网络基础知识(一)wireshark 三次握手实践

    wireshark 三次握手简介 192.168.18.120 IP地址为我的本机虚拟机IP地址 过滤设置:ip.addr == 192.168.18.120 (ip.addr == 192.168. ...

  4. AngularJs入门篇-控制器的加深理解基础篇

    下面做的是一个更新时间的效果,每一秒钟就会更新一下,视图中会显示出当前的时间   下面的这个例子中,SceondController函数将接受两个参数,既该DOM元素的$scope和$timeout. ...

  5. shell ssh 批量执行

    ssh 批量执行命令 #版本1 #!/bin/bash while read line do Ip=`echo $line|awk '{print $1}'` Passwd=`echo $line|a ...

  6. c++刷题(39/100)笔试题3

    题目1: 现在你需要用一台奇怪的打字机书写一封书信.信的每行只能容纳宽度为100的字符,也就是说如果写下某个字符会导致行宽超过100,那么就要另起一行书写 信的内容由a-z的26个小写字母构成,而每个 ...

  7. Chrome插件笔记之content_scripts

    一.概论 说这个之前先看一个段子,讲的是甲方有一奇葩客户,这客户看一网站某些样式很别扭不得劲,非要让乙方修改,乍一听没毛病,但关键是这网站不是乙方家的,根本没有修改权限,怎么办,客户就是上帝,上帝的要 ...

  8. Docker01 CentOS配置Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互之间不会有任何 ...

  9. 从TFS 删除工作项

    一.进入命令执行窗口的方式 1.若你的TFS服务器上安装了VS开发工作,可以按以下步骤,进入命令行方式,图如下: 2.若你TFS上没有安装VS开发工作,可以从你的开发机器上的VS安装目录下(一般情况下 ...

  10. 关于spring中Assert的应用(方法入参检测工具类)

    关于spring中Assert的应用(方法入参检测工具类) Web 应用在接受表单提交的数据后都需要对其进行合法性检查,如果表单数据不合法,请求将被驳回.类似的,当我们在编写类的方法时,也常常需要对方 ...