Spark中的Broadcast处理

首先先来看一看broadcast的使用代码:

val values = List[Int](1,2,3)

val broadcastValues = sparkContext.broadcast(values)

rdd.mapPartitions(iter => {

broadcastValues.getValue.foreach(println)

})

在上面的代码中,首先生成了一个集合变量,把这个变量通过sparkContext的broadcast函数进行广播,

最后在rdd的每个partition的迭代时,使用这个广播变量.

接下来看看广播变量的生成与数据的读取实现部分:

def broadcast[T: ClassTag](value: T): Broadcast[T] = {

  assertNotStopped()

  if (classOf[RDD[_]].isAssignableFrom(classTag[T].runtimeClass)) {

这里要注意,使用broadcast时,不能直接对RDD进行broadcast的操作.

    // This is a warning instead of an exception in order to avoid breaking

//       user programs that

    // might have created RDD broadcast variables but not used them:

    logWarning("Can not directly broadcast RDDs; instead, call collect() and "

      + "broadcast the result (see SPARK-5063)")

  }

通过broadcastManager中的newBroadcast函数来进行广播.

  val bc = env.broadcastManager.newBroadcast[T](value, isLocal)

  val callSite = getCallSite

  logInfo("Created broadcast " + bc.id + " from " + callSite.shortForm)

  cleaner.foreach(_.registerBroadcastForCleanup(bc))

  bc

}

在BroadcastManager中生成广播变量的函数,这个函数直接使用的broadcastFactory的相应函数.

broadcastFactory的实例通过配置spark.broadcast.factory,

默认是TorrentBroadcastFactory.

def newBroadcast[T: ClassTag](value_ : T, isLocal: Boolean): Broadcast[T] = {

  broadcastFactory.newBroadcast[T](value_, isLocal,

nextBroadcastId.getAndIncrement())

}

在TorrentBroadcastFactory中生成广播变量的函数:

在这里面,直接生成了一个TorrentBroadcast的实例.

override def newBroadcast[T: ClassTag](value_ : T, isLocal: Boolean, id: Long)

: Broadcast[T] = {

  new TorrentBroadcast[T](value_, id)

}

TorrentBroadcast实例生成时的处理流程:

这里基本的代码部分是直接写入这个要广播的变量,返回的值是这个变量所占用的block的个数.

Broadcast的block的大小通过spark.broadcast.blockSize配置.默认是4MB,

Broadcast的压缩是否通过spark.broadcast.compress配置,默认是true表示启用,默认情况下使用snappy的压缩.

private val broadcastId = BroadcastBlockId(id)

/** Total number of blocks this broadcast variable contains. */

private val numBlocks: Int = writeBlocks(obj)

接下来生成一个lazy的属性,这个属性仅仅有在详细的使用时,才会运行,在实例生成时不运行(上面的演示样例中的getValue.foreach时运行).

@transient private lazy val _value: T = readBroadcastBlock()

override protected def getValue() = {

  _value

}

看看实例生成时的writeBlocks的函数:

private def writeBlocks(value: T): Int = {

这里先把这个广播变量保存一份到当前的task的storage中,这样做是保证在读取时,假设要使用这个广播变量的task就是本地的task时,直接从blockManager中本地读取.

  SparkEnv.get.blockManager.putSingle(broadcastId, value,

StorageLevel.MEMORY_AND_DISK,

    tellMaster = false)

这里依据block的设置大小,对value进行序列化/压缩分块,每个块的大小为blocksize的大小,

  val blocks =

    TorrentBroadcast.blockifyObject(value, blockSize, SparkEnv.get.serializer,

compressionCodec)

这里把序列化并压缩分块后的blocks进行迭代,存储到blockManager中,

  blocks.zipWithIndex.foreach { case (block, i) =>

    SparkEnv.get.blockManager.putBytes(

      BroadcastBlockId(id, "piece" + i),

      block,

      StorageLevel.MEMORY_AND_DISK_SER,

      tellMaster = true)

  }

这个函数的返回值是一个int类型的值,这个值就是序列化压缩存储后block的个数.

  blocks.length

}

在我们的演示样例中,使用getValue时,会运行实例初始化时定义的lazy的函数readBroadcastBlock:

private def readBroadcastBlock(): T = Utils.tryOrIOException {

  TorrentBroadcast.synchronized {

    setConf(SparkEnv.get.conf)

这里先从local端的blockmanager中直接读取storage中相应此广播变量的内容,假设能读取到,表示这个广播变量已经读取过来或者说这个task就是广播的本地executor.

    SparkEnv.get.blockManager.getLocal(broadcastId).map(_.data.next()) match {

      case Some(x) =>

        x.asInstanceOf[T]

以下这部分运行时,表示这个广播变量在当前的executor中是第一次读取,通过readBlocks函数去读取这个广播变量的全部的blocks,反序列化后,直接把这个广播变量存储到本地的blockManager中,下次读取时,就能够直接从本地进行读取.

      case None =>

        logInfo("Started reading broadcast variable " + id)

        val startTimeMs = System.currentTimeMillis()

        val blocks = readBlocks()

        logInfo("Reading broadcast variable " + id + " took" +

Utils.getUsedTimeMs(startTimeMs))



        val obj = TorrentBroadcast.unBlockifyObject[T](

          blocks, SparkEnv.get.serializer, compressionCodec)

        // Store the merged copy in BlockManager so other tasks on this executor don't

        // need to re-fetch it.

        SparkEnv.get.blockManager.putSingle(

          broadcastId, obj, StorageLevel.MEMORY_AND_DISK, tellMaster = false)

        obj

    }

  }

}

最后再看看readBlocks函数的处理流程:

private def readBlocks(): Array[ByteBuffer] = {

这里定义的变量用于存储读取到的block的信息,numBlocks是广播变量序列化后所占用的block的个数.

  val blocks = new Array[ByteBuffer](numBlocks)

  val bm = SparkEnv.get.blockManager

这里開始迭代读取每个block的内容,这里的读取是先从local中进行读取,假设local中没有读取到数据时,通过blockManager读取远端的数据,通过读取这个block相应的location从这个location去读取这个block的内容,并存储到本地的blockManager中.最后,这个函数返回读取到的blocks的集合.

, numBlocks))) {

    val pieceId = BroadcastBlockId(id, "piece" + pid)

    logDebug(s"Reading piece $pieceId of $broadcastId")



    def getLocal: Option[ByteBuffer] = bm.getLocalBytes(pieceId)

    def getRemote: Option[ByteBuffer] = bm.getRemoteBytes(pieceId).map { block =>

      SparkEnv.get.blockManager.putBytes(

        pieceId,

        block,

        StorageLevel.MEMORY_AND_DISK_SER,

        tellMaster = true)

      block

    }

    val block: ByteBuffer = getLocal.orElse(getRemote).getOrElse(

      throw new SparkException(s"Failed to get $pieceId of $broadcastId"))

    blocks(pid) = block

  }

  blocks

}

spark中的广播变量broadcast的更多相关文章

  1. 025 Spark中的广播变量原理以及测试(共享变量是spark中第二个抽象)

    一:来源 1.说明 为啥要有这个广播变量呢. 一些常亮在Driver中定义,然后Task在Executor上执行. 如果,有多个任务在执行,每个任务需要,就会造成浪费. 二:共享变量的官网 1.官网 ...

  2. 入门大数据---Spark累加器与广播变量

    一.简介 在 Spark 中,提供了两种类型的共享变量:累加器 (accumulator) 与广播变量 (broadcast variable): 累加器:用来对信息进行聚合,主要用于累计计数等场景: ...

  3. Spark(八)【广播变量和累加器】

    目录 一. 广播变量 使用 二. 累加器 使用 使用场景 自定义累加器 在spark程序中,当一个传递给Spark操作(例如map和reduce)的函数在远程节点上面运行时,Spark操作实际上操作的 ...

  4. Android系统中的广播(Broadcast)机制简要介绍和学习计划

    在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制:这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用:本文通过一个 ...

  5. Spark大师之路:广播变量(Broadcast)源代码分析

    概述 近期工作上忙死了--广播变量这一块事实上早就看过了,一直没有贴出来. 本文基于Spark 1.0源代码分析,主要探讨广播变量的初始化.创建.读取以及清除. 类关系 BroadcastManage ...

  6. Spark 广播变量BroadCast

    一. 广播变量 广播变量允许程序员将一个只读的变量缓存在每台机器上,而不用在任务之间传递变量.广播变量可被用于有效地给每个节点一个大输入数据集的副本.Spark还尝试使用高效地广播算法来分发变量,进而 ...

  7. Spark大师之路:广播变量(Broadcast)源码分析

    概述 最近工作上忙死了……广播变量这一块其实早就看过了,一直没有贴出来. 本文基于Spark 1.0源码分析,主要探讨广播变量的初始化.创建.读取以及清除. 类关系 BroadcastManager类 ...

  8. spark累加器、广播变量

    一言以蔽之: 累加器就是只写变量 通常就是做事件统计用的 因为rdd是在不同的excutor去执行的 你在不同excutor中累加的结果 没办法汇总到一起 这个时候就需要累加器来帮忙完成 广播变量是只 ...

  9. Spark2.0基于广播变量broadcast实现实时数据按天统计

    package com.gm.hive.SparkHive; import java.text.SimpleDateFormat; import java.util.Arrays; import ja ...

随机推荐

  1. Uva 11021(概率)

    题意:有k只麻球,每只只能活一天,但临死之前可能产生新麻球,生出i个麻球的概率为pi,给定m,求m天后所有麻球都死亡的概率 输入格式 输入一行为测试数据的组数T,每组数据第一行为3个整数n,k,m;已 ...

  2. 【转】不要使用SBJSON(json-framework)

    原文网址:http://blog.devtang.com/2012/05/05/do-not-use-sbjson/ 不知道为什么,在iOS开发中,有很多人使用 SBJSON (又被称作json-fr ...

  3. EOJ 2822 内存显示

    一个 int 类型变量或 double 类型变量在连续几个字节的内存中存放.读取数值时,当数值中包含小数点时类型为 double,否则类型为 int.将读入的数值存放在 int 类型变量或 doubl ...

  4. [JavaEE] IBM - Spring 系列: Spring 框架简介

    Spring AOP 和 IOC 容器入门 在这由三部分组成的介绍 Spring 框架的系列文章的第一期中,将开始学习如何用 Spring 技术构建轻量级的.强壮的 J2EE 应用程序.develop ...

  5. 第4章 部署模式 Three-Tiered Distribution(三级分布)

    影响因素 Tiered Distribution 中讨论的影响因素也适用于此模式.有关这些通用影响因素的讨论,请参阅"Tiered Distribution".下列影响因素仅适用于 ...

  6. 关于PHP中的SESSION技术

    Session是服务器端技术,服务器在运行时可以为每一个用户创建一个其独享的session文件,所以用户在访问服务器web资源时,可以把各自的数据放在各自的session中,当用于再去访问该服务器中其 ...

  7. (链接)IDEA 2018 激活 IDEA 2018.3激活教程 最新的(三种)—2018.11.26亲测

    破解不成功的请注意时效性,写于2019/2/8,以下第一种激活方法亲测可用, 不过有时候破解成功了可能过几天突然就打不开了,双击无反应的说,这时候再按顺序 操作一遍就是了: 1)把idea64.exe ...

  8. JavaScript实现鼠标效果

    <html> <head> <meta charset="utf-8"> <title>无标题文档</title> &l ...

  9. (转) 50个CSS技巧

    这里我工作中收集了10个很不错的CSS技巧,你可以用在你的项目上.它可以帮你很好地整理你的元素并让他们看起来蛮酷的.下面开始我们的内容,希望你会喜欢它.下面是我收集的CSS技巧,希望能帮助到你,感觉收 ...

  10. VTK+MFC 系列教程 非常强大

    虽然QT才是王道!MFC的懂一些也是好的. 原文链接:http://blog.csdn.net/www_doling_net/article/details/8939115 之前介绍了基于VTK的单文 ...