博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

spark学习记录之withColumn重复计算

Posted on 2022-09-16 18:59  Antel  阅读(428)  评论(0编辑  收藏  举报

在使用Spark,尤其是Spark SQL时,经常会出现一些奇奇怪怪的效率低下问题。比如说,如果lineage比较长的时候,或者lineage比较复杂需要shuffle的时候,可能存在一定的rdd复用问题。

通常在需要复用一个rdd的时候,建议进行persist。但是在实际情况下,又会经常出现不确定是否存在复用的问题。(主要对Spark理解不够深)

下面举一个例子进行学习

image

将箭头指向的map称作map1,右边两个map称作map2.

可以看到,rdd5是由rdd3和rdd4进行union得到,而rdd3和rdd4都最终来自于rdd1,rdd1到rdd2的map1实际承担了两条线路,所以按道理来说应该会执行两次。

rdd1 = sc.parallelize([[i, i+100] for i in range(10)], numSlices=50)
accumulator = sc.accumulator(0)

def map1(x):
    accumulator.add(1)
    return x

def map2(x):
    return x


rdd2 = rdd1.map(map1)
# rdd2.persist()

rdd3 = rdd2.map(map2)
rdd4 = rdd2.map(map2)

rdd5 = rdd3.union(rdd4)

rdd5.collect()
print(accumulator.value)

输出为20。因为rdd1中有10个元素,对应两遍map让累加器变为20.

如果对rdd2进行persist,这样就切断rdd2之前的dag,所以map1只需计算一遍。累加器的计算结果果然为10。

再看Spark SQL

用DataFrame实现上述步骤

df1 = spark.createDataFrame([[i, i+100] for i in range(10)]).toDF("a", 'v').repartition(50)
accumulator = sc.accumulator(0)

@udf
def map1(x):
    accumulator.add(1)
    return x

@udf
def map2(x):
    return x

df2 = df1.withColumn("b", map1("a"))

df3 = df2.withColumn("c", map2("b"))
df4 = df2.withColumn("c", map2("b"))
df5 = df3.union(df4)

df5.collect()
print(accumulator.value)

输出结果竟然是40?

先把df2.persist()一下看看,输出结果为10,这就合理多了。

但是为什么会出现40这个结果。原来是withColumn这个函数使用不当。。。。

学艺不精,惭愧。

df5.columns为['a', 'v', 'b', 'c']

因为是union而来,'b'列执行了两次map1,'c'由'b'经map2得到,'c'因为'b'也需要两次map1,所以一共4次,输出40。

如果不需要'b',仅select出['a', 'v', 'c'], 这样只需要两次,输出结果也与预期一样是20.

归根结底,最后还是要看DataFrame用到了哪些数据,这些列是怎么来的。