spark中的广播变量broadcast
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的更多相关文章
- 025 Spark中的广播变量原理以及测试(共享变量是spark中第二个抽象)
一:来源 1.说明 为啥要有这个广播变量呢. 一些常亮在Driver中定义,然后Task在Executor上执行. 如果,有多个任务在执行,每个任务需要,就会造成浪费. 二:共享变量的官网 1.官网 ...
- 入门大数据---Spark累加器与广播变量
一.简介 在 Spark 中,提供了两种类型的共享变量:累加器 (accumulator) 与广播变量 (broadcast variable): 累加器:用来对信息进行聚合,主要用于累计计数等场景: ...
- Spark(八)【广播变量和累加器】
目录 一. 广播变量 使用 二. 累加器 使用 使用场景 自定义累加器 在spark程序中,当一个传递给Spark操作(例如map和reduce)的函数在远程节点上面运行时,Spark操作实际上操作的 ...
- Android系统中的广播(Broadcast)机制简要介绍和学习计划
在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制:这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用:本文通过一个 ...
- Spark大师之路:广播变量(Broadcast)源代码分析
概述 近期工作上忙死了--广播变量这一块事实上早就看过了,一直没有贴出来. 本文基于Spark 1.0源代码分析,主要探讨广播变量的初始化.创建.读取以及清除. 类关系 BroadcastManage ...
- Spark 广播变量BroadCast
一. 广播变量 广播变量允许程序员将一个只读的变量缓存在每台机器上,而不用在任务之间传递变量.广播变量可被用于有效地给每个节点一个大输入数据集的副本.Spark还尝试使用高效地广播算法来分发变量,进而 ...
- Spark大师之路:广播变量(Broadcast)源码分析
概述 最近工作上忙死了……广播变量这一块其实早就看过了,一直没有贴出来. 本文基于Spark 1.0源码分析,主要探讨广播变量的初始化.创建.读取以及清除. 类关系 BroadcastManager类 ...
- spark累加器、广播变量
一言以蔽之: 累加器就是只写变量 通常就是做事件统计用的 因为rdd是在不同的excutor去执行的 你在不同excutor中累加的结果 没办法汇总到一起 这个时候就需要累加器来帮忙完成 广播变量是只 ...
- Spark2.0基于广播变量broadcast实现实时数据按天统计
package com.gm.hive.SparkHive; import java.text.SimpleDateFormat; import java.util.Arrays; import ja ...
随机推荐
- redis配置文件参数详解
配置文件参数说明: 1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 daemonize no 2. 当Redis以守护进程方式运行时,Redis默认会把pi ...
- [JavaEE] Mybatis与Ibatis比较
随着开发团队转投Google Code旗下,ibatis3.x正式更名为Mybatis 虽然从正式版发布至今也有近一年时间,官方也非常友好的提供了中文版的使用手册,不过相信很多人还在项目中使用ibat ...
- NVL和NVL2有什么区别,NULLIF 的使用.
NULL指的是空值,或者非法值. NVL (expr1, expr2):expr1为NULL,返回expr2:不为NULL,返回expr1.注意两者的类型要一致 NVL2 (expr1, expr2, ...
- NSKeyedUnarchiver归档
把自定义的类对象编码到NSData中 NSData *data = [NSKeyedArchiver archivedDataWithRootObject:bc];//归档,bc是一个自定义的类对象, ...
- OpenCascade 边界表示法(BRep)
转自 http://www.cppblog.com/eryar/archive/2013/08/20/202678.html Topology and Geometry in OpenCascade- ...
- 【OpenCV】关于 waitKey()的使用方法
C++: int waitKey(int delay=0) cvWaitKey()函数的功能是不断刷新图像,频率时间为delay,单位为ms. 返回值为当前键盘按键值. 所以显示图像时,如果需要在cv ...
- Python 中文注释报错解决方法
代码中一旦有了中文注释便会报错. 原因 如果文件里有非ASCII字符,需要在第一行或第二行指定编码声明. 解决方法 在第一行或是第二行加入这么一句# -- coding: utf-8 -- 完美解决
- 原生sql的各种问题
1.nutz有方法自动根据数据库建models吗?2.select * from a a没有建相应的models怎么取结果?3.可以直接操作result,而不是在callback里面设置吗? wend ...
- vue常用方法
vue移动端ui库: http://mint-ui.github.io/#!/zh-cn vue做app开发使用: weex 官网地址:http://weex.apache.org/cn 1.Vue组 ...
- map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
var numbers = [1, 4, 9]; var roots = numbers.map(Math.sqrt); // roots的值为[1, 2, 3], numbers的值仍为[1, 4, ...