Java操作spark-自定义累加器

Java操作spark-自定义累加器

spark的累加器

背景:

如果我们在Driver端定义一个变量,然后将该变量发送Executor端进行累加赋值操作,那么Driver端的变量值会发生改变吗?答案是不会,因为Executor端操作的是变量的副本,并不能影响Driver端的变量值。如何在这样的分布式系统中实现变量的共写呢?这就要用到累加器

简介:
累加器:分布式共享只写变量。(Executor和Executor之间不能读数据

累加器是Spark 计算框架为了能够进行高并发和高吞吐的数据处理封装的三大数据结构之一,功能是实现分布式共享只写变量。累加器用来把 Executor 端变量信息聚合到 Driver 端。在 Driver 程序中定义的变量,在Executor 端的每个 Task 都会得到这个变量的一份新的副本,每个 task 更新这些副本的值后,传回 Driver 端进行merge。以此来实现变量的共写。

在这里插入图片描述
需要注意的是:

Executor端的任务不能读取累加器的值(例如:在Executor端调用sum.value,获取的值不是累加器最终的值)。因此我们说,累加器是一个分布式共享只写变量。

累加器要放在行动算子中,因为转换算子执行的次数取决于job的数量,如果一个spark应用有多个行动算子,那么转换算子中的累加器可能会发生不止一次更新,导致结果错误。所以,如果想要一个无论在失败还是重复计算时都绝对可靠的累加器,我们必须把它放在foreach()这样的行动算子中。

在很多场合我们都可以巧妙利用累加器替代转换算子实现一些功能,避免转换算子带来的shuffle操作,从而提升程序性能

自定义累加器实现wordCount

网上找到的都是scala语言,java语言代码如下:

package com.cjy.bigdata.spark.core.wc;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.util.AccumulatorV2;

import java.util.*;

/**
 * @author cjy
 * @description: 累加器实现wordCount
 * @date: 2022$ 06/17$
 */
public class LearnAccumulator {
    public static void main(String[] args) {
        //环境准备
        SparkConf sparkConf = new SparkConf().setAppName("java word count").setMaster("local");
        JavaSparkContext jsc = new JavaSparkContext(sparkConf);
        List<String> strings = Arrays.asList("hello java", "hello world", "hello spark");
        JavaRDD<String> javaRDD = jsc.parallelize(strings);
        //创建累加器
        MyAccumulator myAccumulator = new MyAccumulator();
        //注册累加器
        jsc.sc().register(myAccumulator,"my");
        javaRDD.foreach(datas -> {
            //使用累加器
            myAccumulator.add(datas);
        });
        //获取结果
        Map<String, Integer> value = myAccumulator.value();
        value.forEach((k,v) -> {
            System.out.println(k + ":" + v);
        });

    }
}

class MyAccumulator extends AccumulatorV2<String, Map<String, Integer>>{

    //定义输出类型变量
    private Map<String ,Integer> output = new HashMap<>();

    //是否为初始状态
    @Override
    public boolean isZero() {
        return this.output.isEmpty();
    }

    //复制累加器
    @Override
    public AccumulatorV2<String, Map<String, Integer>> copy() {
        MyAccumulator myAccumulator = new MyAccumulator();
        //将此累加器中的数据赋值给新创建的累加器
        myAccumulator.output = this.output;
        return myAccumulator;
    }

    //重置累加器
    @Override
    public void reset() {
        this.output.clear();
    }

    //累加器添加元素
    @Override
    public void add(String v) {
        String[] split = v.split(" ");
        for (String s : split){
            //存在则加一,不存在则为一
            int value = this.output.getOrDefault(s, 0) + 1;
            this.output.put(s,value);
        }
    }

    //合并累加器元素
    @Override
    public void merge(AccumulatorV2<String, Map<String, Integer>> other) {
        other.value().forEach((k,v) -> {
            if (this.output.containsKey(k)){
                Integer i1 = this.output.get(k);
                Integer i2 = other.value().get(k);
                this.output.put(k,i1+i2);
            }else {
                this.output.put(k,v);
            }
        });
    }

    //输出
    @Override
    public Map<String, Integer> value() {
        return this.output;
    }
}

运行结果如下:
在这里插入图片描述
用大数据集测试一下累加器的好处,主要是消除shuffle。使用600万的数据,将上述代码中的strings替换成如下表示。

List<String> strings = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 6000000; i++) {
            int salt = random.nextInt(3000000);
            strings.add("asd"+salt+"fdksl");
        }

ui界面图如下:时间是20s,未发生shuffle。
在这里插入图片描述
通常的reduceByKey结果图如下:可见shuffle机制导致时间的延长。
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spark中的自定义函数包括三种类型:udf、udaf和udtf。 1. udf(User-Defined Function):用户自定义函数,用于对DataFrame中的每个元素进行单独的处理,返回一个新的值。可以使用Scala、Java或Python编写。 2. udaf(User-Defined Aggregate Function):用户自定义聚合函数,用于对DataFrame中的一组元素进行聚合操作,返回一个新的值。可以使用Scala、Java或Python编写。 3. udtf(User-Defined Table-Generating Function):用户自定义表生成函数,用于将一行数据转换为多行数据,返回一个新的DataFrame。只能使用Scala或Java编写。 这些自定义函数可以帮助我们更好地处理数据,提高Spark的处理效率和灵活性。 ### 回答2: Spark 是大数据处理中一款极为流行的计算框架,自带的函数库(UDF)非常有限,无法满足大规模数据处理需求,因此需要 Spark 自定义函数(UDF)来弥补这一不足。自定义函数分为三种类型:UDF、UDAF、UDTF。 UDF(User-Defined Function)即用户自定义函数,是一种对 RDDDataFrame 数据进行处理的自定义函数。使用 UDF,可以用编写的代码扩展 Spark 的现有函数库,使其支持更为复杂的操作,提高工作效率。使用 UDF 可以通过嵌套 SQL 或是反射来创建一个函数。UDF 主要通过 Spark SQL 来进行使用,对于 Python 程序员来说还有 UDF 对象模型。 UDAF(User-Defined Aggregation Function)即用户自定义聚合函数。UDAF 可以更好地封装用户自定义聚合函数过程,提高代码复用率,把整个聚合过程封装到一个函数中,便于调用和维护。通常使用 UDAF 构造聚合表达式并将其应用于 Spark SQL 查询。在使用聚合操作时,用户可以指定自定义函数,一般使用聚合函数配合 Spark SQL 或是 API 来使用。 UDTF(User-Defined Table-Generating Function)即用户自定义表格生成函数,可以将一个输入行拆分成多个输出行,还可以通过 UDTF 将一个输入列转化成多个输出列。UDTF 操作有助于负责多输出格式和分割的情况下,实现强大的集合任务文件解析和行转换。与 UDF 和 UDAF 类似,UDTF 可以在调用函数时使用 Apply 函数。UDTF 可以返回多个 Row 对象,并将其转换为新的 DataFrame。UDTF 可以将一行拆分成多行,进行数据拆分和处理的任务。 总而言之,自定义函数是一个非常强大的工具,可以扩展 Spark 的能力,提高计算效率和工作效率。通过三种类型的自定义函数(UDF、UDAF、UDTF),Spark 可以更方便地进行数据处理和分析,使这个框架具备更灵活的应用能力。 ### 回答3: Spark是一种分布式计算框架,其生态圈非常丰富。在Spark中,我们可以使用自定义函数(User Defined Function,简称UDF)、自定义聚合函数(User Defined Aggregate Function,简称UDAF)及自定义表生成函数(User Defined Table Generating Function,简称UDTF)来满足特定的需求。 UDF是Spark中最常用的自定义函数,特别适合对单个列或多个列进行简单转换的场景。UDF可以用Scala、Java或Python等语言来编写。在Scala或Java中定义UDF时,需要定义一个函数,并将它与SparkSession的udf()方法一起使用。在Python中,UDF的定义基于通用Python函数,使用Python的decorators来描述该函数的功能。 UDAF是用于聚合多个值的自定义函数。UDAF的好处是可以以两种不同的方式来使用:作为聚合函数或开窗函数。Spark提供了两种UDAF:typed aggregates和untyped aggregates。typed aggregates是一种类型安全的操作,可以通过将多个值组合在一起来处理。untyped aggregates是一种无类型的操作,需要我们自己来处理所有细节。 UDTF是用于生成几个结果表的自定义函数。在使用UDTF时,我们需要定义一个新的中间表来存储结果,然后将中间表传递给Spark SQL的from()方法,以创建最终结果。 无论使用哪种自定义函数,我们都需要考虑性能因素。因为我们的数据通常分布在多个计算节点上,所以不合理的计算可能会导致结果不准确或性能下降。另外,我们还需要确保我们的自定义函数能够处理大型数据集,并且具有足够的容错能力。 总之,Spark中的自定义函数可以帮助我们实现一些常规操作以外的数据处理需求。通过UDF、UDAF和UDTF,我们可以根据具体的场景设计出高效、可靠的数据处理方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值