PySpark RDD的broadcast join
关于spark broadcast join,网上一堆文章讲述其原理,这里就不赘述了。
网上也有很多代码实现。但是,1. 很多代码实现,没有考虑join的各种复杂情况,用的例子是一一对应的简单join。而且很多代码是基于sparksql的,而使用sparksql本身就很多坑。2. 大多实现是java和scala版本,没看到有python版本的。
总之,用PySpark的话,按照下面的模板实现rdd broadcast join就好了。
PySpark版本的broadcast join
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("rdd broadcast join").getOrCreate()
sc = spark.sparkContext
# small_rdd = sc.parallelize([(1, "sm1"), (2, "sm2"), (3, "sm3")])
# 考虑小rdd中的key和大rdd不契合的情况
small_rdd = sc.parallelize([(1, "sm1"), (3, "sm3"), (4, "sm4")])
large_rdd = sc.parallelize([(1, 10), (2, 95), (1, 48), (3, 68), (2, 92),
(1, 33),(2, 32), (3, 28), (1, 114), (2, 88)])
# small_rdd小数据,collect到Driver中,因为broadcast只接受数组类型
# 然后使用Spark的广播功能,将小RDD的数据转成广播变量,发送到每个Executor中存储
broadcast_small_table = sc.broadcast(small_rdd.collect())
# 从Executor获取被广播的小表数据,存入字典中,便于后面join操作
small_table_dict = dict(broadcast_small_table.value)
"""
很多例子用的简单直接的处理,没有考虑join过程中key匹配不到的情况,产生的空值要做处理。
自定义方法分别是spark rdd的broadcast innerjoin和broadcast leftjoin
line: map操作的每一行
srdd_dict: 小表broadcast数据的字典格式
join_type: join类型
"""
def myJoin(line, srdd_dict, join_type):
key = line[0]
value = line[1]
value_small = srdd_dict[key] if key in srdd_dict else None
if join_type == "inner":
return (line[0], (value_small, value)) if value_small is not None else None
elif join_type == "left":
return (line[0], (value_small if value_small is not None else "-1", value))
r_rdd_inner = large_rdd.map(lambda line: myJoin(line, small_table_dict, "inner")).filter(lambda line: line is not None)
print(r_rdd_inner.collect())
r_rdd_left = large_rdd.map(lambda line: myJoin(line, small_table_dict, "left")).filter(lambda line: line is not None)
print(r_rdd_left.collect())