函数使用rdd.map()映射到RDD的某些行被多次调用

时间:2019-04-02 17:42:26

标签: python apache-spark pyspark rdd

我有一个包含一些记录的源数据框。我想对该数据帧的每一行执行一些操作。为此,使用了rdd.map函数。但是,查看使用累加器记录的日志,看起来某些行多次调用了映射函数。根据文档,只能调用一次。

我尝试用一​​个小的脚本复制该问题,并注意到相同的行为。该脚本如下所示:

import os
import sys
os.environ['SPARK_HOME'] = "/usr/lib/spark/"
sys.path.append("/usr/lib/spark/python/")
from pyspark.sql import *
from pyspark.accumulators import AccumulatorParam


class StringAccumulatorParam(AccumulatorParam):
    def zero(self, initialValue=""):
        return ""

    def addInPlace(self, s1, s2):
        return s1.strip() + " " + s2.strip()

def mapped_func(row, logging_acc):
    logging_acc += "Started map"
    logging_acc += str(row)
    return "test"

if __name__ == "__main__":
    spark_session = SparkSession.builder.enableHiveSupport().appName("rest-api").getOrCreate()
    sc = spark_session.sparkContext
    df = spark_session.sql("select col1, col2, col3, col4, col5, col6 from proj1_db.dw_table where col3='P1'")
    df.show()
    logging_acc = sc.accumulator("", StringAccumulatorParam())
    result_rdd = df.rdd.map(lambda row: Row(row, mapped_func(row, logging_acc)))
    result_rdd.toDF().show()
    print "logs: " + str(logging_acc.value)

以下是相关的输出:

+----+----+----+----+----+----+
|col1|col2|col3|col4|col5|col6|
+----+----+----+----+----+----+
|   1|   1|  P1|   2|  10|  20|
|   3|   1|  P1|   1|  25|  25|
+----+----+----+----+----+----+

+--------------------+----+
|                  _1|  _2|
+--------------------+----+
|[1, 1, P1, 2, 10,...|test|
|[3, 1, P1, 1, 25,...|test|
+--------------------+----+

logs: Started map Row(col1=1, col2=1, col3=u'P1', col4=2, col5=10, col6=20) Started map Row(col1=1, col2=1, col3=u'P1', col4=2, col5=10, col6=20) Started map Row(col1=3, col2=1, col3=u'P1', col4=1, col5=25, col6=25)

第一个表是源数据帧,第二个表是在映射函数调用之后创建的结果数据帧。 如图所示,该函数在第一行被调用两次。谁能帮助我了解正在发生的事情,以及如何确保映射的函数每行仅调用一次。

1 个答案:

答案 0 :(得分:1)

  

根据文档,仅应调用一次。

事实并非如此。任何转换都可以执行任意次(通常在发生故障或支持辅助逻辑的情况下)和the documentation says explicitly that

  

对于仅在操作中执行的累加器更新,Spark保证每个任务对累加器的更新将仅应用一次

因此,转换中使用的隐式累加器(如map)可以针对每个任务多次更新。

在您的情况下,由于将RDD转换为DataFrame时不提供架构,因此会发生多次执行。在这种情况下,Spark将执行另一次数据扫描以从数据推断架构,即

spark.createDataFrame(result_rdd, schema)

但是,这只会解决这个特定问题,而有关转换和累加器行为的一般观点仍然存在。