火花累加器值未增加

时间:2019-03-16 10:37:31

标签: java apache-spark user-defined-functions apache-spark-dataset accumulator

我最近一直在处理Spark数据集,在这种情况下,我必须为每一行生成行号并将其存储在名为“ Ids”的列中。此行号从1开始,2、3 ...,并根据数据集中的行数递增。 (在我的情况下,有10000-20000条记录)

考虑一下,我有一个具有值的数据集'empDataset':

name , dept , project
---------------------
Tina, Finance , abc
Leena, Finance , abc
Joe, Marketing , xyz

现在对于上述数据集,我想添加一个列“ Ids”,其值从1,2,3 ..等递增。

预期输出是

name , dept , project ,Ids
--------------------------
Tina, Finance , abc , 1
Leena, Finance , abc , 2
Joe, Marketing , xyz , 3

我还想将此输出存储在另一个数据集中,并进一步用于不同的转换。

需要帮助来解决这个问题。!

我的代码段:

LongAccumulator  accValue = spark.sparkContext().longAccumulator();
long rowNumber = 1;

spark.udf().register("randomNumberGenerator", new UDF1<String, Long>() {

            @Override
            public Long call(String namCol) throws Exception {
                    accum.add(rowNumber);
                    System.out.println("inside" + accum.value());
                    return accum.value();
                }
        }, DataTypes.LongType);

Dataset<Row> empDatasetWithIds= empDataset.withColumn("Ids",callUDF("randomNumberGenerator",
                col(name)));

Dataset<Row> filterDept = empDatasetWithIds.filter(...here filtering with dept...)

我得到的输出是empDatasetWithIds(输出不正确):

name , dept , project ,Ids
--------------------------
Tina, Finance , abc , 1
Leena, Finance , abc , 2
Joe, Marketing , xyz , 1

在本地模式下运行时,上面的代码可以正常工作,但在群集模式下,这些值不会增加。

我还浏览了以下链接: https://community.hortonworks.com/questions/36888/spark-java-accumulator-not-incrementing.html Spark Java Accumulator not incrementing

火花累积器需要采取行动来触发作业。在我的场景中,我将进一步对数据集执行过滤器转换,如何解决此问题。需要帮助。

3 个答案:

答案 0 :(得分:1)

累加器是用于跨执行器累积数据并将其发送回驱动程序的变量。如果您从执行程序中读取其值,则该行为未定义(AFAIK)。我想您可能会得到到目前为止本地分区已累积的内容。实际上,spark的目标是进行并行计算。因此,当使用累加器时,每个分区的数据都在单独的累加器中累加,然后合并并发送回驱动程序(map reduce范式)。因此,您不能使用累加器在执行程序之间共享信息。那不是什么意思

但是,您可以做的是,如果需要连续的索引,则可以使用RDD API中的zipWithIndex,如果需要连续的索引,则可以使用SparkSQL API中的monoticallyIncreasingId。前者触发一个小的火花作业,而后者几乎是免费的(无火花作业)。

选项1(增加但不一定是连续的索引)

yourDataframe.withColumn("id", functions.monotonicallyIncreasingId());

选项2(连续和递增的索引)

StructType schema = yourDataframe.schema();
schema.add(new StructField("id", DataTypes.LongType, false,null));
JavaRDD<Row> rdd = yourDataframe.toJavaRDD().zipWithIndex()
    .map(x -> {
         Collection<Object> row = JavaConverters.asJavaCollection(x._1.toSeq());
         Long index = x._2;
         row.add(index);
         return RowFactory.create(row);
    });
Dataset<Row> indexedData = spark.createDataFrame(rdd, schema);

答案 1 :(得分:0)

如果顺序升序不是问题,则可以按照以下说明简单地进行操作:

import org.apache.spark.sql.functions.monotonically_increasing_id 
import spark.implicits._

val ds = sc.parallelize(Seq(
    ("a", -1.0), ("b", -2.0), ("c", -3.0)),5).toDS   // Just a dummy DS

val newds = ds.withColumn("uniqueIdColumn", monotonically_increasing_id())

newds.show(false)

尝试一下,以适应自己的情况。

顺便说一句:错误使用蓄电池。

答案 2 :(得分:-1)

为此功能,您可以使用row_number

import org.apache.spark.sql.expressions.Window
import static org.apache.spark.sql.functions.col;
import static org.apache.spark.sql.functions.row_number;

Dataset<Row> empDatasetWithIds = empDataset.withColumn("Ids", 
    row_number().over(Window.orderBy(col("name"), col("dept"), col("project)))
)

参考: https://stackoverflow.com/a/31077759

如注释中所指出,使用不带分区的Window效率很低。并且应避免在生产代码中处理大数据。

您对累加器的处理不起作用(如Why does worker node not see updates to accumulator on another worker nodes?中所述),因为spark在不同的执行程序(在不同计算机上运行的不同jvm进程)中运行此代码,并且每个累加器都有自己的副本。