spark 笔记 16: BlockManager

spark 笔记 16: BlockManager
先看一下原理性的文章:http://jerryshao.me/architecture/2013/10/08/spark-storage-module-analysis/ ,http://jerryshao.me/architecture/2013/10/08/spark-storage-module-analysis/  , 另外,spark的存储使用了Segment File的概念(http://en.wikipedia.org/wiki/Segmented_file_transfer ),概括的说,它是把文件划分成多个段,分别存储在不同的服务器上;在读取的时候,同时从这些服务器上读取。(这也是BT的基础)。
之前分析shuffle的调用关系的时候,其实已经包含了很多的BlockManager的流程,但还是有必要系统的看一遍它的代码。
getLocalFromDisk这个函数,是前面看shuffleManager的终点,但却是BlockManager的起点。即使是到远端获取block的操作,也是发送一个消息到远端服务器上执行getLocalFromDisk,然后再把结果发送回来。
->diskStore.getValues(blockId, serializer)

============================BlockManager============================
-> BlockManager::getLocalFromDisk
->diskStore.getValues(blockId, serializer)
->getBytes(blockId).map(bytes => blockManager.dataDeserialize(blockId, bytes, serializer))
->val segment = diskManager.getBlockLocation(blockId) --DiskBlockManager的方法,获取block在一个文件中的一个块位置
->if  blockId.isShuffle and env.shuffleManager.isInstanceOf[SortShuffleManager] --如果是hash类型shuffle,
->sortShuffleManager.getBlockLocation(blockId.asInstanceOf[ShuffleBlockId], this) --For sort-based shuffle, let it figure out its blocks
->else if blockId.isShuffle and shuffleBlockManager.consolidateShuffleFiles --联合文件模式
->shuffleBlockManager.getBlockLocation(blockId.asInstanceOf[ShuffleBlockId]) --For hash-based shuffle with consolidated files
->val shuffleState = shuffleStates(id.shuffleId) --
->for (fileGroup <- shuffleState.allFileGroups)
->val segment = fileGroup.getFileSegmentFor(id.mapId, id.reduceId) --次函数单独分析
->if (segment.isDefined) { return segment.get }
->else
->val file = getFile(blockId.name)--getFile(filename: String): File
->val hash = Utils.nonNegativeHash(filename)
->val dirId = hash % localDirs.length
->val subDirId = (hash / localDirs.length) % subDirsPerLocalDir
->var subDir = subDirs(dirId)(subDirId)
->new File(subDir, filename)
->new FileSegment(file, 0, file.length())
->val channel = new RandomAccessFile(segment.file, "r").getChannel
->if (segment.length < minMemoryMapBytes)
->channel.position(segment.offset)
->channel.read(buf)
->return buf
->else
->return Some(channel.map(MapMode.READ_ONLY, segment.offset, segment.length))

ShuffleFileGroup:如何通过mapId和reduceId在ShuffleBlockManager 中获取数据:getFileSegmentFor函数
->根据reduceId从ShuffleFileGroup的属性val files: Array[File]里面找到reduce的文件句柄fd
    ->根据mapId从mapIdToIndex找到index,
        ->根据reduce找到blockOffset向量和blockLen向量,
            ->再通过index从向量里面找到offset和len,
                ->最后通过offset和len从fd里面读取到需要的数据

从远本地取数据
->BlockManager::doGetLocal
->val info = blockInfo.get(blockId).orNull
->val level = info.level
->if (level.useMemory) --Look for the block in memory
->val result = if (asBlockResult)
->memoryStore.getValues(blockId).map(new BlockResult(_, DataReadMethod.Memory, info.size))
->esle
->memoryStore.getBytes(blockId)
->if (level.useOffHeap) -- Look for the block in Tachyon
->tachyonStore.getBytes(blockId)
->if (level.useDisk)
->val bytes: ByteBuffer = diskStore.getBytes(blockId)
->if (!level.useMemory) // If the block shouldn't be stored in memory, we can just return it
->if (asBlockResult)
->return Some(new BlockResult(dataDeserialize(blockId, bytes), DataReadMethod.Disk, info.size))
->else
->return Some(bytes)
->else --memory// Otherwise, we also have to store something in the memory store
->if (!level.deserialized || !asBlockResult) 不序列化或者不block"memory serialized", or if it should be cached as objects in memory
->val copyForMemory = ByteBuffer.allocate(bytes.limit)
->copyForMemory.put(bytes)
->memoryStore.putBytes(blockId, copyForMemory, level)
->if (!asBlockResult)
->return Some(bytes)
->else --需要序列化再写内存
->val values = dataDeserialize(blockId, bytes)
->if (level.deserialized) // Cache the values before returning them
->val putResult = memoryStore.putIterator(blockId, values, level, returnValues = true, allowPersistToDisk = false)
->putResult.data match case Left(it) return Some(new BlockResult(it, DataReadMethod.Disk, info.size))
->else
->return Some(new BlockResult(values, DataReadMethod.Disk, info.size))
->val values = dataDeserialize(blockId, bytes)
从远端获取数据
->BlockManager::doGetRemote
->val locations = Random.shuffle(master.getLocations(blockId)) --随机打散
->for (loc <- locations) --遍历所有地址
->val data = BlockManagerWorker.syncGetBlock(GetBlock(blockId), ConnectionManagerId(loc.host, loc.port))
->val blockMessage = BlockMessage.fromGetBlock(msg)
->val newBlockMessage = new BlockMessage()
->newBlockMessage.set(getBlock)
->typ = BlockMessage.TYPE_GET_BLOCK
->id = getBlock.id
->val blockMessageArray = new BlockMessageArray(blockMessage)
-> val responseMessage = Try(Await.result(connectionManager.sendMessageReliably(toConnManagerId, blockMessageArray.toBufferMessage), Duration.Inf))
->responseMessage match {case Success(message) =>  val bufferMessage = message.asInstanceOf[BufferMessage]
->logDebug("Response message received " + bufferMessage)
->BlockMessageArray.fromBufferMessage(bufferMessage).foreach(blockMessage => 
->logDebug("Found " + blockMessage)
->return blockMessage.getData
->return Some(data)

===========================end=================================
再次引用这个图:多个map可以对应一个文件,其中每个map对应文件中的某些段。这样做是为了减少文件数量。
spark shuffle  consolidation process
(图片来源:http://jerryshao.me/architecture/2014/01/04/spark-shuffle-detail-investigation/ )
获取block数据返回的数据结构
/* Class for returning a fetched block and associated metrics. */
private[spark] class BlockResult(
val data: Iterator[Any],
readMethod: DataReadMethod.Value,
bytes: Long) {
val inputMetrics = new InputMetrics(readMethod)
inputMetrics.bytesRead = bytes
}

private[spark] class BlockManager(
executorId: String,
actorSystem: ActorSystem,
val master: BlockManagerMaster,
defaultSerializer: Serializer,
maxMemory: Long,
val conf: SparkConf,
securityManager: SecurityManager,
mapOutputTracker: MapOutputTracker,
shuffleManager: ShuffleManager)
extends BlockDataProvider with Logging {
shuffle状态,主要包含了unusedFileGroups、allFileGroups两个属性,记录当前已经使用和未使用的ShuffleFileGroup
/**
* Contains all the state related to a particular shuffle. This includes a pool of unused
* ShuffleFileGroups, as well as all ShuffleFileGroups that have been created for the shuffle.
*/
private class ShuffleState(val numBuckets: Int) {
val nextFileId = new AtomicInteger(0)
val unusedFileGroups = new ConcurrentLinkedQueue[ShuffleFileGroup]()
val allFileGroups = new ConcurrentLinkedQueue[ShuffleFileGroup]()

/**
* The mapIds of all map tasks completed on this Executor for this shuffle.
* NB: This is only populated if consolidateShuffleFiles is FALSE. We don't need it otherwise.
*/
val completedMapTasks = new ConcurrentLinkedQueue[Int]()
}
shuffleStates 是一个基于时间戳的hash table 
private val shuffleStates = new TimeStampedHashMap[ShuffleId, ShuffleState]

private val metadataCleaner =
new MetadataCleaner(MetadataCleanerType.SHUFFLE_BLOCK_MANAGER, this.cleanup, conf)
Used by sort-based shuffle: shuffle结束时将结果注册到shuffleStates
/**
* Register a completed map without getting a ShuffleWriterGroup. Used by sort-based shuffle
* because it just writes a single file by itself.
*/
def addCompletedMap(shuffleId: Int, mapId: Int, numBuckets: Int): Unit = {
shuffleStates.putIfAbsent(shuffleId, new ShuffleState(numBuckets))
val shuffleState = shuffleStates(shuffleId)
shuffleState.completedMapTasks.add(mapId)
}
将自己注册给master 
/**
* Initialize the BlockManager. Register to the BlockManagerMaster, and start the
* BlockManagerWorker actor.
*/
private def initialize(): Unit = {
master.registerBlockManager(blockManagerId, maxMemory, slaveActor)
BlockManagerWorker.startBlockManagerWorker(this)
}
从本地磁盘获取一个block数据。为了方便使用
/**
* A short-circuited method to get blocks directly from disk. This is used for getting
* shuffle blocks. It is safe to do so without a lock on block info since disk store
* never deletes (recent) items.
*/
def getLocalFromDisk(blockId: BlockId, serializer: Serializer): Option[Iterator[Any]] = {
diskStore.getValues(blockId, serializer).orElse {
throw new BlockException(blockId, s"Block $blockId not found on disk, though it should be")
}
}

ShuffleWriterGroup:每个shuffleMapTask都有一组shuffleWriter,它给每个reducer分配了一个writer。当前只有HashShufflle使用了,唯一一个实例化是在forMapTask返回的,给HashShuffleWriter的shuffle属性使用:
/** A group of writers for a ShuffleMapTask, one writer per reducer. */
private[spark] trait ShuffleWriterGroup {
val writers: Array[BlockObjectWriter]

/** @param success Indicates all writes were successful. If false, no blocks will be recorded. */
def releaseWriters(success: Boolean)
}

/**
* Manages assigning disk-based block writers to shuffle tasks. Each shuffle task gets one file
* per reducer (this set of files is called a ShuffleFileGroup).
*
* As an optimization to reduce the number of physical shuffle files produced, multiple shuffle
* blocks are aggregated into the same file. There is one "combined shuffle file" per reducer
* per concurrently executing shuffle task. As soon as a task finishes writing to its shuffle
* files, it releases them for another task.
* Regarding the implementation of this feature, shuffle files are identified by a 3-tuple:
* - shuffleId: The unique id given to the entire shuffle stage.
* - bucketId: The id of the output partition (i.e., reducer id)
* - fileId: The unique id identifying a group of "combined shuffle files." Only one task at a
* time owns a particular fileId, and this id is returned to a pool when the task finishes.
* Each shuffle file is then mapped to a FileSegment, which is a 3-tuple (file, offset, length)
* that specifies where in a given file the actual block data is located.
*
* Shuffle file metadata is stored in a space-efficient manner. Rather than simply mapping
* ShuffleBlockIds directly to FileSegments, each ShuffleFileGroup maintains a list of offsets for
* each block stored in each file. In order to find the location of a shuffle block, we search the
* files within a ShuffleFileGroups associated with the block's reducer.
*/
// TODO: Factor this into a separate class for each ShuffleManager implementation
private[spark]
class ShuffleBlockManager(blockManager: BlockManager,
shuffleManager: ShuffleManager) extends Logging {
ShuffleFileGroup是一组文件,每个reducer对应一个。每个map将会对应一个这个文件(但多个map可以对应一个文件)。多个map对应一个文件时,它们写入是分段写入的(mapId,ReduceId)通过getFileSegmentFor函数获取到这个块的内容
privateobject /**
* .
* .
*/
private class val Int, val Int, val private var numBlocksInt 0

/**
* For instance,
* if mapId 5 is the first block in each file, mapIdToIndex(5) = 0.
*/
private val mapIdToIndex new Int, Int/**
* Stores consecutive offsets and lengths of blocks into each reducer file, ordered by
* position in the file.
* Note: * .
*/
private val blockOffsetsByReducer fillLongnew Longprivate val blockLengthsByReducer fillLongnew Longdef applyIntdef recordMapOutputInt, Long, LongassertmapIdToIndexnumBlocks
numBlocks 1
for 0 blockOffsetsByReducerblockLengthsByReducer/** Returns the FileSegment associated with the given map task, or None if no entry exists. */
def getFileSegmentForInt, Intval val blockOffsetsByReducerval blockLengthsByReducerval mapIdToIndex, 1if 0val val Somenew , , else











来自为知笔记(Wiz)


posted on 2015-01-27 16:20 过雁 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/zwCHAN/p/4253287.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259435.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python的异常处理

python的try语句有两种风格 一&#xff1a;种是处理异常&#xff08;try/except/else&#xff09; 二&#xff1a;种是无论是否发生异常都将执行最后的代码&#xff08;try/finally&#xff09; try/except/else风格 try: <语句> #运行别的代码 except <名字>&…

解决SQL单用户模式不能转为多用户模式

数据库CS 转为单用户模式后&#xff0c;却不能访问属性&#xff0c;一直想不通&#xff0c;但毕竟是测试用的&#xff0c;也就没放心上。网上找到段代码可以恢复多用户模式。却还是不能解决不能访问单用户属性的问题。USE master;GoDECLARE SQL VARCHAR(MAX);SET SQLSELECT SQL…

C++ stringstream介绍,使用方法与例子

C引入了ostringstream、istringstream、stringstream这三个类&#xff0c;要使用他们创建对象就必须包含sstream.h头文件。   istringstream类用于执行C风格的串流的输入操作。 ostringstream类用于执行C风格的串流的输出操作。 strstream类同时可以支持C风格的串流的输入…

xp下添加linux启动,如何在windows xp系统下安装linux???

我刚刚想开始学linux&#xff0c;请教如何安装&#xff01;|我今天才安装了Redhat 9.0。LINUX不可能在WINDOWS下安装。比较简单的方法是先在XP下用PQMAGIC分好区boot ext3 100M , / ext3 6G , swap 内存1&#xff0d;2倍详细见www.linuxfans.org linux安装说明最后&#xf…

linux rar安装

描述&#xff1a;Linux默认自带ZIP压缩&#xff0c;最大支持4GB压缩&#xff0c;RAR的压缩比大于4GB. 流程&#xff1a;下载 》安装 》 使用 -------------------------------------------------- 下载 # wget http://www.rarsoft.com/rar/rarlinux-x64-5.2.1b1.tar.gz--16:01:…

hoj 2739 中国邮局问题

1 /*若原图的基图不连通,2 或者存在某个点的入度或出度为 0 则无解。3 统计所有点的入度出度之差 Di, 对于 Di > 0 的点,4 加边(s, i, Di, 0); 对于 Di < 0 的点加边(i, t, -Di,0);5 对原图中的每条边(i, j),6 在网络中加边(i, j, ∞, Dij),Dij 为边(i, j)的权值。7 求一…

R语言编程艺术(3)R语言编程基础

本文对应《R语言编程艺术》 第7章&#xff1a;R语言编程结构&#xff1b; 第9章&#xff1a;面向对象的编程&#xff1b; 第13章&#xff1a;调试 R语言编程结构 控制语句&#xff1a; 循环&#xff1a; for (n in x) { } while (condition) { } repeat { }另外break也可以用在…

用C++流成员函数put输出单个字符

转载&#xff1a;http://c.biancheng.net/cpp/biancheng/view/254.html 在程序中一般用cout和插入运算符“<<”实现输出&#xff0c;cout流在内存中有相应的缓冲区。有时用户还有特殊的输出要求&#xff0c;例如只输出一个字符。ostream类除了提供上面介绍过的用于格式控…

linux 扩充db2表空间,如何扩充db2的表空间、加容器等表空间维护操作

db2 "alter tablespace GJDATA resize (FILE /backup/GJDATA32K45G)"db2 "alter tablespace GJIDX resize (FILE /backup/GJIDX32K45G)"容器路径 db2 list tablespace containers for8容器大小 db2pd -d uibsch -tablespaces降低容器空间 resize 增加容器…

CheckBox控件

前台代码&#xff1a; 1 <asp:CheckBox ID"CheckBox1" runat"server" Text "苹果"/> 2 <asp:CheckBox ID"CheckBox2" runat"server" Text "柠檬"/> 3 <asp:CheckBox ID"CheckBox3" runa…

.NET垃圾回收笔记

名词 垃圾收集目标 ephemeral GC发生在Gen 0 和Gen 1 的垃圾收集 Full GC发生Gen 2 及以上的Gen与LOH的垃圾收集 垃圾收集模式 工作站模式GC直接发生在内存分配的线程&#xff08;也是当前的工作托管线程&#xff09;上 服务器模式每个CPU核都有一个自己独立的GC线程与托管堆 垃…

go.js中的图标(icons)的使用

2019独角兽企业重金招聘Python工程师标准>>> 1、图标库下载&#xff1a; 将icons引入&#xff1a;http://gojs.net/latest/samples/icons.js 2、样式演示 地址&#xff1a;http://gojs.net/latest/samples/icons.html 转载于:https://my.oschina.net/u/2391658/blog…

Pygame - Python游戏编程入门(1)

前言 在上一篇中&#xff0c;我们初步熟悉了pygame的控制流程&#xff0c;但这对于一个游戏而言是远远不够的。所以在这一篇中&#xff0c;我们的任务是添加一架飞机&#xff08;玩家&#xff09;&#xff0c;并且能够控制它进行移动&#xff0c;这样我们就又离目标进了一步了~…

C++字符输入getchar()和字符输出putchar()

转载&#xff1a;http://c.biancheng.net/cpp/biancheng/view/117.html C还保留了C语言中用于输入和输出单个字符的函数&#xff0c;使用很方便。其中最常用的有getchar函数和putchar函数。 putchar函数(字符输出函数) putchar函数的作用是向终端输出一个字符。例如&#xf…

linux实现shell,linux

4.5Mhttp://www.starbase-929.net/media/Calibre%20Library/Ken%20O.%20Bartch/Linux%20Shell%20Scription%20With%20Bash%20(1778)/Linux%20Shell%20Scription%20With%20Bash%20-%20Ken%20O.%20Bartch.pdfstarbase-929.net全网免费4.0Mhttp://www.myaitcampus.net/elibrary/im…

AQS浅析

2019独角兽企业重金招聘Python工程师标准>>> AQS的原理浅析 本文是《Java特种兵》的样章&#xff0c;本书即将由工业出版社出版 AQS的全称为&#xff08;AbstractQueuedSynchronizer&#xff09;&#xff0c;这个类也是在java.util.concurrent.locks下面。这个类似乎…

str045漏洞提权linux,Linux运维知识之CVE-2016-5195 Dirtycow: Linux内核提权漏洞

本文主要向大家介Linux运维知识之CVE-2016-5195 Dirtycow&#xff1a; Linux内核提权漏洞绍了&#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习Linux运维知识有所帮助。CVE-2016-5195 Dirtycow&#xff1a; Linux内核提权漏洞以下都是github上找的源码&#xf…

编程如写作

昨晚似乎是个适合写作的夜&#xff0c;不论是自己还是朋友&#xff0c;都比平常更容易被触动。看着微博上朋友们的心路&#xff0c;想写点什么却似乎找不出非常值得大书特书的主题&#xff0c;只是歪坐在电脑旁&#xff0c;喝着咖啡&#xff0c;单曲循环着仓木麻衣的《time aft…

C++中cin、cin.get()、cin.getline()、getline()等函数的用法

转载&#xff1a;http://www.cnblogs.com/flatfoosie/archive/2010/12/22/1914055.html c输入流函数主要以下几个&#xff1a; 1、cin 2、cin.get() 3、cin.getline() 4、getline() 附:cin.ignore();cin.get()//跳过一个字符,例如不想要的回车,空格等字符 1、cin>>…

工作环境总结(1)开发环境搭建

1、安装git 安装文件&#xff1a;Git-2.12.0-64-bit.exe 下载地址&#xff1a;https://github.com/git-for-windows/git/releases/download/v2.12.0.windows.1/Git-2.12.0-64-bit.exe 在git bash中配置&#xff0c;git bash命令行中执行&#xff08;只有使用到egit时使用&…