Spark 浅谈Spark中的各种join

众所周知,Join的种类丰富:

按照**关联形式(**Join type)划分:

有内关联,外关联,左关联,右关联,半关联,逆关联等,由业务逻辑决定的关联形式决定了Spark任务的运行结果;

按照关联机制(Join Mechanisms)划分:

有NLJ(Nested Loop Join) , SMJ(Sort Merge Join)和HJ(Hash Join),由数据内容决定的实现机制,则决定了Spark任务的运行效率;

关联形式

Spark支持的关联形式:

关联形式Join Type关键字
内连接inner
左外关联left / left outer / left_outer
右外关联right/ right outer/right_outer
全外关联outer/ full / full outer / full_outer
左半关联left semi/ left_semi
左逆关联left anti / left_anti
交叉关联crossJoin

数据准备

import spark.implicits._
import org.apache.spark.sql.DataFrame
 
// 创建员工信息表
val seq = Seq((1, "Mike", 28, "Male"), (2, "Lily", 30, "Female"), (3, "Raymond", 26, "Male"), (5, "Dave", 36, "Male"))
val employees: DataFrame = seq.toDF("id", "name", "age", "gender")
 
// 创建薪资表
val seq2 = Seq((1, 26000), (2, 30000), (4, 25000), (3, 20000))
val salaries:DataFrame = seq2.toDF("id", "salary")

// 左表
salaries.show
 
/** 结果打印
+---+------+
| id|salary|
+---+------+
| 1| 26000|
| 2| 30000|
| 4| 25000|
| 3| 20000|
+---+------+
*/
 
// 右表
employees.show
 
/** 结果打印
+---+-------+---+------+
| id| name|age|gender|
+---+-------+---+------+
| 1| Mike| 28| Male|
| 2| Lily| 30|Female|
| 3|Raymond| 26| Male|
| 5| Dave| 36| Male|
+---+-------+---+------+
*/
内连接(Inner join)

内连接是默认关联形式,可以省略写成join. 左右表按照join key连接, 舍弃未匹配的行,仅仅保留左右表中满足关联条件的那些数据记录.

// 内关联
val jointDF: DataFrame = salaries.join(employees, salaries("id") === employees("id"), "inner")
 
jointDF.show
 
/** 结果打印
+---+------+---+-------+---+------+
| id|salary| id| name|age|gender|
+---+------+---+-------+---+------+
| 1| 26000| 1| Mike| 28| Male|
| 2| 30000| 2| Lily| 30|Female|
| 3| 20000| 3|Raymond| 26| Male|
+---+------+---+-------+---+------+
*/
外连接(Outer join)
val jointDF: DataFrame = salaries.join(employees, salaries("id") === employees("id"), "left")
 
jointDF.show
 
/** 结果打印
+---+------+----+-------+----+------+
| id|salary| id| name| age|gender|
+---+------+----+-------+----+------+
| 1| 26000| 1| Mike| 28| Male|
| 2| 30000| 2| Lily| 30|Female|
| 4| 25000|null| null|null| null|
| 3| 20000| 3|Raymond| 26| Male|
+---+------+----+-------+----+------+
*/

外连接的左右指的是不满足join条件的数据来自于哪张表,上述的"left"左外连接,就让第三行数据来自于左表.

半关联(semi join)
 // 左半关联
val jointDF: DataFrame = salaries.join(employees, salaries("id") === employees("id"), "leftsemi")
 
jointDF.show
 
/** 结果打印
+---+------+
| id|salary|
+---+------+
| 1| 26000|
| 2| 30000|
| 3| 20000|
+---+------+
*/

半关联是inner join的一半返回,left semi join返回左表数据, right semi join返回右表数据

逆关联(anti join)
// 左逆关联
val jointDF: DataFrame = salaries.join(employees, salaries("id") === employees("id"), "leftanti")
 
jointDF.show
 
/** 结果打印
+---+------+
| id|salary|
+---+------+
| 4| 25000|
+---+------+
*/

逆关联返回的是未关联上的行.

关联机制

假设我们将join表称为"驱动表",将被join的表称为"基表",基于这两个称呼:

spark的关联机制

join实现机制工作原理适用场景
Nested Loop Join在驱动表与基表之上,使用嵌套的双层for循环实现关联,效率最低,算法复杂度为O(M*N)其他策略不使用的情况
Sort Merge Join首先将两张表排序,然后以双指针遍历的方式实现关联,关联阶段的算法复杂度为O(M+N)两张表都按照join key排序的情况
Hash Join关联过程分为两个阶段 Build + Probe , Build 使用Hash算法生成哈希表O(N),Probe 查表获得值O(M)哈希表较小且易生成.

关联策略

Spark的关联策略对比

对比项Shuffle JoinBroadcast Join
实现原理* 根据Join Keys 计算哈希值
* 将哈希值按照并行度(parallelism)取模
封装一张小表为广播变量,发送到所有Executor.
优点不论数据的体量是大是小、不管内存是否足够,Shuffle Join 在功能上都能成功地完成数据关联的计算通过分发较小数据表,SQL的执行性能高效.
适用场景任何场景都能完成广播表较小
前提条件基表需要足够小(小于Excutor内存)
缺点shuffle IO 带来的性能瓶颈

关联机制 x 关联策略

3种关联机制跟 2中关联策略的组合,出现了6中join.由于Broadcast SMJ < Broadcast HJ ,去掉毫无用武之处的Broadcast SMJ,余下了以下5种join方式.

join方式原理适用场景
Broadcast HJ基表加工成哈希表,广播到所有Executor,内部查表连接基表小,等值join查表快
Broadcast NLJ广播小表,Excutor内部用双重循环连接基表小,可以用于不等值join
Shuffle SMJ因为Shuffle要按照join key排序,所以spark 优先选择SMJ基表大
Shuffle HJ因为SMJ的原因,Shuffle HJ基本不用同Shuffle SMJ
Shuffle NLJ两张表都比较大,而且join是非等值join其他场景都不适用的情况
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值