spark2实战-使用Spark SQL的Join

概述

join操作在进行数据处理时非常常见,而spark支持多种join类型。本文对spark中多种Join类型进行说明,并对不同join的使用场景进行了介绍和举例说明。

使用join操作的注意事项

  • 在两个数据集比较的列有唯一值,使用默认join(inner join)会有较好的性能,但要注意:两个数据集中不匹配的key值的数据行将会被丢掉,另外,当比较的列有重复值时,会进行排列组合操作,此时可能会衍生出大量的数据,很可能会产生shuffle。
  • 当两个数据集有重复的key时,执行join操作时可能会让数据膨胀,此时,最好先对数据集进行distinct或combineByKey操作来减少key值的数量。也可以使用cogroup来处理重复key,避免产生全局排列组合方式来产生数据。在合并时使用智能分区,可以防止join过程中产生二次shuffle。
  • 若有的key只存在在两个数据集中的一个中,就有可能产生数据丢失,此时使用outer join更加安全。这样可以保证把数据全都保存在其中的一个数据集中,然后再进行过滤。
  • 如果在进行join之前,可以过滤一些key,就尽量过滤掉,这样可以减少join过程中的数据传输量。
  • 注意:join是非常耗资源的操作,所以在进行join之前,应该尽量过滤掉不需要的数据。

join类型说明

inner join(默认类型)

默认的join类型,在Spark SQL中的操作是:t1.Id = t2.Id

  • 适用场景
    • 两个数据集比较的列的值唯一(去重),可以有较好的性能
  • 注意事项
    • 当两个数据集不匹配时,数据将会被丢掉
    • 当比较的列有重复值时,会进行排列组合操作,此时可能会衍生出大量的数据,很可能会产生shuffle。
outer join
  • 使用场景
    • 需要保留全部数据,而不光是匹配上的数据
  • 注意事项
    • 使用outer join产生的数据量可能会比较大
    • 使用outer join后往往会再进行过滤操作
    • 需要根据具体的数据集情况来选择把数据保存在left还是right的数据集中

join操作及其说明

测试数据集

本文使用以下两个测试数据集,如下:

  • left.csv
id,addr
1,a1
2,a2
3,a3
4,a4
10,
11,
12,
  • right.csv
id,name
1,n1
2,n2
3,n3
4,n4
5,n5
6,n6
7,n7
8,n8
9,n9
  • 通过pyspark进行加载和测试
left = spark.read.option("header","true").csv("left.csv")
right = spark.read.option("header","true").csv("right.csv")

通过以上代码得到两个dataframe。如下:

>>> left.show()
+---+----+
| id|addr|
+---+----+
|  1|  a1|
|  2|  a2|
|  3|  a3|
|  4|  a4|
| 10|null|
| 11|null|
| 12|null|
+---+----+

>>> right.show()
+---+----+
| id|name|
+---+----+
|  1|  n1|
|  2|  n2|
|  3|  n3|
|  4|  n4|
|  5|  n5|
|  6|  n6|
|  7|  n7|
|  8|  n8|
|  9|  n9|
+---+----+

通过这两个dataframe来理解一下的join操作。

inner join(默认)

>>> r = left.join(right, left.id== right.id, "inner")
>>> r.show()
+---+----+---+----+
| id|addr| id|name|
+---+----+---+----+
|  1|  a1|  1|  n1|
|  2|  a2|  2|  n2|
|  3|  a3|  3|  n3|
|  4|  a4|  4|  n4|
+---+----+---+----+
# 或
>>> r  = left.join(right, left.id == right.id)
>>> r.show()
+---+----+---+----+
| id|addr| id|name|
+---+----+---+----+
|  1|  a1|  1|  n1|
|  2|  a2|  2|  n2|
|  3|  a3|  3|  n3|
|  4|  a4|  4|  n4|
+---+----+---+----+

从以上操作可以看出,默认的==号操作就是inner join。

要点:
  • 从以上的结果可以看出,凡是id不相等的列都被过滤掉了,所以要注意inner join会把不匹配的数据丢掉。

left outer join

要点:
  • 当id不匹配时,left outer join会保留左边数据集的全部数据。
  • 这样:左边数据集的数据不会丢失。
>>> r = left.join(right, left.id== right.id, "leftouter")
>>> r.show()
+---+----+----+----+
| id|addr|  id|name|
+---+----+----+----+
|  1|  a1|   1|  n1|
|  2|  a2|   2|  n2|
|  3|  a3|   3|  n3|
|  4|  a4|   4|  n4|
| 10|null|null|null|
| 11|null|null|null|
| 12|null|null|null|
+---+----+----+----+
用途:
  • 当处理两张全量表时,有时候会找出两张全量表中的新增id,此时就可以使用left outer join来计算:

    select * from A left outer join  B on (A.id=B.id) where right.id is null 
    

left semi join和left anti join

要点:
  • left semi join只会保留左边数据集的字段。而且需要满足:左边数据集的id必须在右边数据集中存在。

  • left anti join也只会保留左边数据集的字段。而且要满足条件:左边数据集的id不能在右边数据集中。

# left semi join
>>> r = left.join(right, left.id== right.id, "leftsemi")
>>> r.show()
+---+----+
| id|addr|
+---+----+
|  1|  a1|
|  2|  a2|
|  3|  a3|
|  4|  a4|
+---+----+

# left anti join,只保留左边字段,而且id不能在右边数据集存在
>>> r = left.join(right, left.id== right.id, "leftanti")
>>> r.show()
+---+----+
| id|addr|
+---+----+
| 10|null|
| 11|null|
| 12|null|
+---+----+

right join

right join会保留右边数据集的全部记录。包括两部分:(1)和左边数据集匹配的记录,(2)和左边数据集不匹配的记录,此时左边的列会用空填充。

>>> r = left.join(right, left.id== right.id, "right")
>>> r.show()
+----+----+---+----+
|  id|addr| id|name|
+----+----+---+----+
|   1|  a1|  1|  n1|
|   2|  a2|  2|  n2|
|   3|  a3|  3|  n3|
|   4|  a4|  4|  n4|
|null|null|  5|  n5|
|null|null|  6|  n6|
|null|null|  7|  n7|
|null|null|  8|  n8|
|null|null|  9|  n9|
+----+----+---+----+

从以上结果中可以看出,右边数据集的数据被全部保留下来了,这样保证了右边数据集不丢失。

right semi join和right anti join

这两个操作和left semi join以及left anti join相似,不同的是:这两个操作只保留右边数据集的字段。

(这里不再累述。)

full outer join

该outer操作是left outer join和right outer join结果的合集。要注意,该操作可能会非常耗时,需要谨慎使用。

>>> r = left.join(right, left.id== right.id, "fullouter")
>>> r.show()
+----+----+----+----+                                                           
|  id|addr|  id|name|
+----+----+----+----+
|null|null|   7|  n7|
|  11|null|null|null|
|   3|  a3|   3|  n3|
|null|null|   8|  n8|
|null|null|   5|  n5|
|null|null|   6|  n6|
|null|null|   9|  n9|
|   1|  a1|   1|  n1|
|  10|null|null|null|
|   4|  a4|   4|  n4|
|  12|null|null|null|
|   2|  a2|   2|  n2|
+----+----+----+----+

在join时广播小数据集

当一个表很小而另一个表很大时,可以使用broadcast方式来进行join。

>>> r = left.join(F.broadcast(right), left.id== right.id, "rightouter")
>>> r.show()
+----+----+---+----+
|  id|addr| id|name|
+----+----+---+----+
|   1|  a1|  1|  n1|
|   2|  a2|  2|  n2|
|   3|  a3|  3|  n3|
|   4|  a4|  4|  n4|
|null|null|  5|  n5|
|null|null|  6|  n6|
|null|null|  7|  n7|
|null|null|  8|  n8|
|null|null|  9|  n9|
+----+----+---+----+

总结

join操作是比较消耗性能的,在进行join操作时尽可能进行去重,并去掉不需要的数据。另外,可以通过广播小数据集等方式来加速join的过程。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值