共享变量

(1)累加器:是用来对信息进行聚合的,同时也是Spark中提供的一种分布式的变量机制,其原理类似于mapreduce,即分布式的改变,然后聚合这些改变。累加器的一个常见用途是在调试时对作业执行过程中的事件进行计数。 Spark内置的提供了Long和Double类型的累加器

object AccumulatorDemo {
def main(args: Array[String]): Unit = {
//通过conf创建sc 创建Spark配置对象
val sc = new SparkContext(new SparkConf().setMaster("local").setAppName("AccumulatorDemo"))
val file = sc.textFile("file:///F:/spark/c.txt")
val blankLines = sc.longAccumulator("blankLines");
val info = file.flatMap(line => {
if (line == "") {
blankLines.add(1L)
}
line.split(" ")
})
info.saveAsTextFile("file:///F:/spark/out1")
println("BlankLines= " + blankLines)
}
}

此代码用于实现累加文档中的空行数目。输出

BlankLines= LongAccumulator(id: 0, name: Some(blankLines), value: 0)

**需要注意的是:**XXXXAccumulator也是lazy的,在行动操作前的转化操作是不会进行求值的。

这里用一个更好的例子来解释这个lazy的效果。

    val accum = sc.longAccumulator("AccumulatorDemo")
val numberRDD = sc.parallelize(Array(1,2,3,4,5,6,7,8,9),2).map(n=>{
accum.add(1L)
n+1
})
numberRDD.count
println("accum1:"+accum.value)
numberRDD.reduce(_+_)
println("accum2: "+accum.value)

结果如下

accum1:9
accum2: 18

可见,虽然在map里面进行了累加器加1的操作,两个累加值却不一样,这是因为count和reduce都是action,而且第一次count的action并没有持久化,此时的accum的value已经是9了,在下一次的reduce的action中依然要重新计算一次,相当于提交了两次作业,那reduce执行之后accume的value将会从9变为18。如果我们在count的action之前调用persist()(cache())进行持久化,这样的话reduce的action就不需要从头计算了,两次的accum的就会一样了。

自定义累加器:

isZero: 判断是否为初始值

copy:拷贝累加器

add: 操作数据累加方法实现

merge: 合并数据

value: AccumulatorV2对外访问的数据结果 “`

import org.apache.spark.util.AccumulatorV2

/**
* 实现字符串拼接
* */
class MyAccumulator extends AccumulatorV2[String, String] { private var res=""
override def isZero: Boolean = {
res==""
} override def copy(): AccumulatorV2[String, String] ={
val newAcc=new MyAccumulator()
newAcc.res=this.res
newAcc
} override def reset(): Unit = {
res=""
} override def add(v: String): Unit = {
res+=v+'-'
} override def merge(other: AccumulatorV2[String, String]): Unit = other match{
case o:MyAccumulator => res+=o.res
case _=>throw new UnsupportedOperationException(
s"Cannot merge ${this.getClass.getName} with ${other.getClass.getName}")
} override def value: String = res
}

调用实现

def main(args: Array[String]): Unit = {
val sc = new SparkContext(new SparkConf()
.setAppName("Accumulator1")
.setMaster("local")) val myAcc = new MyAccumulator
sc.register(myAcc,"myAcc")
//val acc = sc.longAccumulator("avg")
val nums = Array("a","b","c","d","e","f","h","i")
val numsRdd = sc.parallelize(nums)
numsRdd.foreach(num => myAcc.add(num))
println(myAcc)
sc.stop()
}

执行结果

MyAccumulator(id: 0, name: Some(myAcc), value: a-b-c-d-e-f-h-i-)

(2)广播变量

首先要简单来了解一下闭包的概念:函数可以访问函数外面的变量,但是函数内对变量的修改,在函数外是不可见的。(闭包是一个函数,它返回值取决于在此函数之外声明的一个或多个变量的值。)

Spark的第二种共享变量类型是广播变量,它可以让程序高效的向所有的工作节点发送一个较大的只读值,以供一个或者多个spark操作来使用,广播变量可以解决闭包函数引用外部大变量引起的性能问题,广播变量将只读变量缓存在每个worker节点中,Spark使用了高效广播算法分发变量从而提高通信性能。

广播变量的优势:是因为不是每个task一份变量副本,而是变成每个节点的executor才一份副本。这样的话,就可以让变量产生的副本大大减少。

广播变量,初始的时候,就在Drvier上有一份副本。

task在运行的时候,想要使用广播变量中的数据,此时首先会在自己本地的Executor对应的BlockManager中,尝试获取变量副本;如果本地没有BlockManager,也许会从远程的Driver上面去获取变量副本;也有可能从距离比较近的其他节点的Executor的BlockManager上去获取,并保存在本地的BlockManager中;BlockManager负责管理某Executor对应的内存和磁盘上的数据,此后这个executor上的task,都会直接使用本地的BlockManager中的副本。

例如,50个executor,1000个task。一个map,10M:

默认情况下,1000个task,1000份副本。10G的数据,网络传输,在集群中,耗费10G的内存资源。

如果使用了广播变量。50个execurtor,50个副本。500M的数据,网络传输,而且不一定都是从Driver传输到每个节点,还可能是就近从最近的

节点的executor的bockmanager上拉取变量副本,网络传输速度大大增加;500M,大大降低了内存消耗。

import java.util
import org.apache.spark.{SparkConf, SparkContext}
object testBroadcast {
def main(args: Array[String]): Unit = {
//声明一个SparkContext对象
val sc=new SparkContext(new SparkConf().setMaster("local[*]").setAppName("BroadcastDemo"))
val rdd = sc.parallelize(List("1,张三","0,李四","3,王五"))
val map = new util.HashMap[String,String]()
map.put("1","男人")
map.put("0","女人")
//声明一个广播变量
val bd = sc.broadcast(map)
val rdd1=rdd.map(e=>{
val splits = e.split(",")
val sid = splits(0)
//获取广播变量中的值
val name = bd.value.getOrDefault(sid,"未知")
splits(1)+" is "+name+""
}).cache()
rdd1.foreach(println(_))
}
}

注意: 不能将RDD使用一个广播变量广播出去,因为RDD是不存储数据的。可以将RDD的结果广播出去。通过广播变量的value获取广播变量的值。

参考文章:

https://blog.csdn.net/u013468917/article/details/70617085

https://blog.csdn.net/leen0304/article/details/78866353

http://www.ccblog.cn/103.htm

https://www.cnblogs.com/newdingwei/p/6802972.html

Spark基础:(五)Spark编程进阶的更多相关文章

  1. spark实验(五)--Spark SQL 编程初级实践(1)

    一.实验目的 (1)通过实验掌握 Spark SQL 的基本编程方法: (2)熟悉 RDD 到 DataFrame 的转化方法: (3)熟悉利用 Spark SQL 管理来自不同数据源的数据. 二.实 ...

  2. Day7 - Python基础7 面向对象编程进阶

    Python之路,Day7 - 面向对象编程进阶   本节内容: 面向对象高级语法部分 经典类vs新式类 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个 ...

  3. Python基础7 面向对象编程进阶

    本节内容: 面向对象高级语法部分 经典类vs新式类 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 经典 ...

  4. Python基础-week06 面向对象编程进阶

    一.反射 1.定义:指的是通过字符串来操作类或者对象的属性 2.为什么用反射? 减少冗余代码,提升代码质量. 3.如何用反射? class People: country='China' def __ ...

  5. Spark 基础操作

    1. Spark 基础 2. Spark Core 3. Spark SQL 4. Spark Streaming 5. Spark 内核机制 6. Spark 性能调优 1. Spark 基础 1. ...

  6. Spark菜鸟学习营Day3 RDD编程进阶

    Spark菜鸟学习营Day3 RDD编程进阶 RDD代码简化 对于昨天练习的代码,我们可以从几个方面来简化: 使用fluent风格写法,可以减少对于中间变量的定义. 使用lambda表示式来替换对象写 ...

  7. Spark函数式编程进阶

    函数式编程进阶 1.函数和变量一样作为Scala语言的一等公民,函数可以直接复制给变量: 2.函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但是匿名函数赋值给 ...

  8. Scala实战高手****第12课:Scala函数式编程进阶(匿名函数、高阶函数、函数类型推断、Currying)与Spark源码鉴赏

    /** * 函数式编程进阶: * 1.函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量 * 2.函数更常用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称 ...

  9. Spark学习之编程进阶——累加器与广播(5)

    Spark学习之编程进阶--累加器与广播(5) 1. Spark中两种类型的共享变量:累加器(accumulator)与广播变量(broadcast variable).累加器对信息进行聚合,而广播变 ...

  10. Spark编程基础_RDD初级编程

    摘要:Spark编程基础_RDD初级编程 RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变.可分区.里面的元素 ...

随机推荐

  1. iostat主要性能指标

    iostat参数很多,日常运维中主要关注一下字段(根据这些字段的输出内容一般就可以确定服务器是否存在IO性能瓶颈) 1.%iowait:CPU等待输入输出完成时间的百分比.该值较高,表示磁盘存在I/O ...

  2. BugKu之备份是个好习惯

    题目:备份是个好习惯 思路分析 打开题目,看到一个字符串. 联系到题目,就猜到肯定是源代码泄露,用工具扫一下,发现了index.php.bak,验证了我的猜想,下载下来看看. <?php /** ...

  3. buuoj刷题 October

    2019 极客大挑战 web easysql 直接万能密码登就完事了 LoveSQL 万能密码登进去,给了md5,没解出来 手注吧,都要忘了手注怎么注了 猜字段数 3的时候正常不报错 看回显位,2,3 ...

  4. .NET Protobuf包装器库

    Wodsoft Protobuf Wrapper 内容 关于 需求 安装 用法 序列化 反序列化 字段定义 字段排序 非空构造函数对象 获取Protobuf包装器 高级 支持的属性类型与Protobu ...

  5. java miniui(datagrid) 取值赋值

    ... (本想放项目代码上来的 可是这放上去就看不了不知为何,下面是项目中的代码) // 获取时间设置到时间上,加载没有那个快,所以需要先加载完数据然后才能获取 function obtain(e) ...

  6. 菜鸡的Java笔记 第六 - java 方法

    前提:现在所讲解的方法定义格式,只属于JAVA 方法定义的其中一种组成方式.而完整的组成方式将随着学习逐步渗透. 1.方法的基本定义 方法(Method)在一些书中也会有人将其说是  函数(Funct ...

  7. 菜鸡的Java笔记 第九 - java 接收键盘输入

    package mysterious; import java.util.Scanner; public class lianxi { public static void hhh (){ Scann ...

  8. 在 Kubernetes 上安装 Gitlab CI Runner Gitlab CI 基本概念以及 Runner 的安装

    简介 从 Gitlab 8.0 开始,Gitlab CI 就已经集成在 Gitlab 中,我们只要在项目中添加一个.gitlab-ci.yml文件,然后添加一个Runner,即可进行持续集成.在介绍 ...

  9. Spark SQL知识点大全与实战

    Spark SQL概述 1.什么是Spark SQL Spark SQL是Spark用于结构化数据(structured data)处理的Spark模块. 与基本的Spark RDD API不同,Sp ...

  10. 【JavaSE】异常

    Java异常 2019-07-06  22:16:29  by冲冲 1. 引例 任何程序都有出错的可能.比如代码少一个分号,那么运行的结果是 java.lang.Error.比如运行 System.o ...