跟我一起学Spark之——Spark进阶编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunWuKong_Hadoop/article/details/87277886

6.1简介

主要介绍两种类型的共享变量:累加器(accumulator)、广播变量(broadcast variable)

累加器用来对信息进行聚合,广播变量用来高效分发较大的对象。

我们使用Spark共享变量来对非严重错误的情况进行计数,以及分发一张巨大的查询表。

当任务需要很长时间进行配置,譬如需要创建数据库连接或者随机数生成器时,在多个数据元素间共享一次配置就会比较有效率。

敲黑板!!!!(如何基于分区进行操作以重用数据库连接的配置)

6.2累加器

常见用途:在调试时对作业执行过程中的事件进行计数。

例如:我们从文件中读取号码列表对应的日志,同时也想知道输入文件中有多少空行。

val sc = new SparkContext()
val file = sc.textFile("file.txt")
val blanklines = sc.accumulator(0)
val callSigns = file.flatMap(line => {
  if(line == ""){
    blanklines += 1}
  line.split(" ")
})
callSigns.saveAsTextFile("output.txt")
println("Blank lines:" + blanklines.value)

要点:创建一个Accumulator[Int]对象,在输入中看到一个空行时就对其加1.

敲黑板!!!!

  1. 只有在运行saveAsTextFile( )行动操作后才能看到正确的计数,因为行动操作前的转化操作flatmap( )是惰性的,所以作为计算副产品的累加器只有在惰性的转化操作flatMap( )被saveAsTextFile( )行动操作强制触发时才会开始求值。
  2. 工作节点上的任务不能访问累加器的值。(从这些任务的角度来看,累加器是一个只写变量)
  3. 可使用累加器对有效记录和无效记录分别进行计数。(累加器的值只有在驱动器程序中访问,所以检查也应当其中完成)

总结累加器的用法:

  1. 通过在驱动器中调用SparkContext.accumulator(initialValue)方法,创建出存有初始值的累加器。返回值为org.apache.spark.Accumulator[T]对象,其中T是初始值initialValue的类型。
  2. Spark闭包里的执行器代码可以使用累加器的+=方法(在Jave中是add)增加累加器的值。
  3. 驱动器程序可以调用累加器的value属性(在Java中使用value( )或setValue( ))来访问累加器的值。

6.2.1累加器与容错性

Spark会自动重新执行失败的或较慢的任务来应对有有错误的或者比较慢的机器。

即使节点没有失败,Spark有时也需要重新运行任务来获取缓存中被移除出内存的数据。因此最终结果就是同一个函数可能对同一个数据运行了多次,这取决于集群发生了生么。

累加器实际处理:对于要在行动操作中使用的累加器,Spark只会把每个任务对各累加器的修改应用一次。因此,如果想要一个无论在失败还是重复计算时都绝对可靠的累加器,我们必须把它放在foreach( )这样的行动操作中。

但是对于在RDD转化操作中使用的累加器,就不能保证有这种情况了。转化操作中的累加器可能会发生不止一次的更新。

1.2.0版本中转化操作中的累加器最好只在调试时使用。

6.2.2自定义累加器

http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.package

6.3广播变量

可以让程序高效地向所有工作节点发送一个较大的只读值,以供一个或多个Spark操作使用。例如:

如果你的应用需要向所有节点发送一个较大的只读查询表,甚至是机器学习算法中的一个很大的特征向量,广播变量用起来都很顺手。

Spark会自动把闭包中所有引用到的变量发送到工作节点上,虽然这很方便,但也很低效!原因:

  1. 默认的任务发射机制是专门为小任务进行优化的;
  2. 事实上你可能会在多个并行操作中使用同一个变量,但是Spark会为每个操作分别发送。

例如:val signPrefixes = loadCallSignTable() 可修改为 val signPrefixes = sc.broadcast(loadCallSignTable())

  1. 通过对一个类型T的对象调用SparkContext.broadcast创建出一个Broadcast[T]对象。任何可序列化的类型都可这样实现。
  2. 通过value属性访问该对象的值(在Java中为value()方法)。
  3. 变量只会被发到各个节点一次,应作为只读值处理(修改这个值不会影响到别的节点)。

6.3.1广播的优化

当广播一个比较大的值时,选择既快又好的序列化格式,Java序列化库对于除基本类型的数组以外的任何对象都比较低效。

你可以使用spark.serializer属性选择另一个序列化库来优化序列化过程(另见Kryo这种更快的序列化库),也可以为你的数据类型实现自己的序列化方式(对Java对象使用java.io.Externalizable接口实现序列化,或使用reduce( )方法为Python的pickle库定义自定义的序列化)。

6.4基于分区进行操作

更新日期:20190409

为避免为每个数据元素进行重复的配置,Spark提供基于分区的map和foreach,让你的部分代码只对RDD的每个分区进行一次操作

出处:《Spark快速大数据分析》作者:Holden Karau

猜你喜欢

转载自blog.csdn.net/SunWuKong_Hadoop/article/details/87277886
今日推荐