spark存储管理之磁盘存储--DiskStore
DiskStore
接着上一篇,本篇,我们分析一下实现磁盘存储的功能类DiskStore,这个类相对简单。在正式展开之前,我觉得有必要大概分析一下BlockManager的背景,或者说它的运行环境,运行的作用范围。Blockmanager这个类其实在运行时的每个节点都会有一个实例(包括driver和executor进程),因为不论是driver端进行广播变量的创建,还是executor端shuffle过程中写shuffle块,或者是任务运行时结果太大需要通过BlockManager传输,或者是RDD的缓存,其实在每个运行节点上都会通过Blockmanager来管理程序内部对于本地的内存和磁盘的读写,所以综上,我想表达的核心意思就是每个进程(driver和executor)都有一Blockmanager实例,而这些Blockmanager实例是通过BlockManagerId类来进行唯一区分的,BlockManagerId实际上是对进程物理位置的封装。
DiskStore.put
首先我们来看一个最常用的写入方法
def put(blockId: BlockId)(writeFunc: WritableByteChannel => Unit): Unit = {
// 通过DiskBlockManager对象检查这个blockId对应的文件名的文件是否存在
if (contains(blockId)) {
throw new IllegalStateException(s"Block $blockId is already present in the disk store")
}
logDebug(s"Attempting to put block $blockId")
val startTime = System.currentTimeMillis
// 通过DiskBlockManager获取一个文件用于写入数据
val file = diskManager.getFile(blockId)
// 用CountingWritableChannel包装一下,以便于记录写入的字节数
val out = new CountingWritableChannel(openForWrite(file))
var threwException: Boolean = true
try {
writeFunc(out)
// 关键步骤,记录到内部的map结构中
blockSizes.put(blockId, out.getCount)
threwException = false
} finally {
try {
out.close()
} catch {
case ioe: IOException =>
if (!threwException) {
threwException = true
throw ioe
}
} finally {
if (threwException) {
remove(blockId)
}
}
}
val finishTime = System.currentTimeMillis
logDebug("Block %s stored as %s file on disk in %d ms".format(
file.getName,
Utils.bytesToString(file.length()),
finishTime - startTime))
}
这个方法很简单,没什么好说的,但是调用了一个比较重要的类DiskBlockManager,这个类的功能就是对磁盘上的目录和文件进行管理,会在磁盘上按照一定规则创建一些目录和子目录,在分配文件名时也会尽量均匀第分配在这些目录和子目录下。
DiskStore.putBytes
这个方法就不说了,简单处理一下直接调用put方法。
def putBytes(blockId: BlockId, bytes: ChunkedByteBuffer): Unit = {
put(blockId) { channel =>
bytes.writeFully(channel)
}
}
DiskStore.getBytes
我们来看一下这个方法,首先通过DiskBlockManager获取对应的文件名,然后将其包装成一个BlockData对象,分为加密和不加密两种。
def getBytes(blockId: BlockId): BlockData = {
val file = diskManager.getFile(blockId.name)
val blockSize = getSize(blockId)
securityManager.getIOEncryptionKey() match {
case Some(key) =>
// Encrypted blocks cannot be memory mapped; return a special object that does decryption
// and provides InputStream / FileRegion implementations for reading the data.
new EncryptedBlockData(file, blockSize, conf, key)
case _ =>
// 看一下DiskBlockData
new DiskBlockData(minMemoryMapBytes, maxMemoryMapBytes, file, blockSize)
}
}
DiskBlockData
这个类作为磁盘文件的包装类,主要功能是提供了几个方便的接口,将磁盘文件中的数据读取出来并生成缓冲对象。
这个类中有两个重要的方法toChunkedByteBuffer和toByteBuffer,toByteBuffer就不说了,调用ReadableByteChannel.read(ByteBuffer dst)方法读取文件数据,我们看一下toChunkedByteBuffer
DiskBlockData.toChunkedByteBuffer
这个方法也很简单,在数据量比较大的时候,由于每次申请的内存块大小有限制maxMemoryMapBytes,所以需要切分成多个块
override def toChunkedByteBuffer(allocator: (Int) => ByteBuffer): ChunkedByteBuffer = {
// Utils.tryWithResource调用保证在使用完资源后关闭资源
// 基本等同于java中的try{}finally{}
Utils.tryWithResource(open()) { channel =>
var remaining = blockSize
val chunks = new ListBuffer[ByteBuffer]()
while (remaining > 0) {
// 这里取剩余大小和maxMemoryMapBytes的较小值,
// 也就是说每次申请的内存块大小不超过maxMemoryMapBytes
val chunkSize = math.min(remaining, maxMemoryMapBytes)
val chunk = allocator(chunkSize.toInt)
remaining -= chunkSize
JavaUtils.readFully(channel, chunk)
chunk.flip()
chunks += chunk
}
new ChunkedByteBuffer(chunks.toArray)
}
}
DiskBlockManager
这个类之前也分析过,主要是用来管理spark运行过程中写入的一些临时文件,以及目录的管理。
首先会根据参数配置创建本地目录(可以是逗号分隔的多个目录),参数的优先顺序是:如果是运行在yarn上,则会使用yarn参数LOCAL_DIRS配置的本地目录;否则获取环境变量SPARK_LOCAL_DIRS的值;否则获取spark.local.dir参数的值;最后如果都没有配置,那么就用java系统参数java.io.tmpdir的值作为临时目录。
其次,关于文件在目录之间分配的问题,使用文件名的hash值对目录数量取余的方法来尽量将文件均匀地分配到不同的目录下。
另外一点要说的是文件名的命名规则,是根据不同作用的Block来区别命名的,例如RDD缓存写入的block的id就是RDDBlockId,它的文件名拼接规则是"rdd_" + rddId + "_" + splitIndex
spark存储管理之磁盘存储--DiskStore的更多相关文章
- Spark存储管理(读书笔记)
Spark存储管理(读书笔记) 转载请注明出处:http://www.cnblogs.com/BYRans/ Spark的存储管理 RDD的存放和管理都是由Spark的存储管理模块实现和管理的.本文从 ...
- Spark 概念学习系列之Spark存储管理机制
Spark存储管理机制 概要 01 存储管理概述 02 RDD持久化 03 Shuffle数据存储 04 广播变量与累加器 01 存储管理概述 思考: RDD,我们可以直接使用而无须关心它的实现细节, ...
- spark 存储管理机制
累加器 -- Accumulators 广播变量--Broadcast Variables 思考 回顾 存储管理模块架构--从架构上来看 存储管理模块架构--通信层 存储管理模块架构--存储层 存储管 ...
- Spark源码阅读之存储体系--存储体系概述与shuffle服务
一.概述 根据<深入理解Spark:核心思想与源码分析>一书,结合最新的spark源代码master分支进行源码阅读,对新版本的代码加上自己的一些理解,如有错误,希望指出. 1.块管理器B ...
- Spark源码剖析 - SparkContext的初始化(八)_初始化管理器BlockManager
8.初始化管理器BlockManager 无论是Spark的初始化阶段还是任务提交.执行阶段,始终离不开存储体系.Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈,优先将配置信息.计算结 ...
- Spark源码分析之九:内存管理模型
Spark是现在很流行的一个基于内存的分布式计算框架,既然是基于内存,那么自然而然的,内存的管理就是Spark存储管理的重中之重了.那么,Spark究竟采用什么样的内存管理模型呢?本文就为大家揭开Sp ...
- Spark学习笔记--概念知识
RDD被视为由不同的数据块组成,对于RDD的存取是以数据块为单位的,本质上分区(partition)和数据块(block)是等价的,只是看待的角度不同. 数据块 Spark存储管理模块中所管理的几种主 ...
- Spark分布式编程之全局变量专题【共享变量】
转载自:http://www.aboutyun.com/thread-19652-1-1.html 问题导读 1.spark共享变量的作用是什么?2.什么情况下使用共享变量?3.如何在程序中使用共享变 ...
- spark总结——转载
转载自: spark总结 第一个Spark程序 /** * 功能:用spark实现的单词计数程序 * 环境:spark 1.6.1, scala 2.10.4 */ // 导入相关类库impor ...
随机推荐
- 201871020225-牟星源 《面向对象程序设计(java)》课程学习进度条
<2019面向对象程序设计(java)课程学习进度条> 周次 (阅读/编写)代码行数 发布博客量/评论他人博客数量 课余学习时间(小时) 学习收获最大的程序 阅读或编译让我 第一周 25/ ...
- Comet OJ 夏季欢乐赛 完全k叉树
完全k叉树 https://cometoj.com/contest/59/problem/A?problem_id=2712 题目描述 欢迎报考JWJU!这里有丰富的社团活动,比如为梦想奋斗的ACM集 ...
- 洛谷p1458顺序的分数题解
抱歉,您们的蒟蒻yxj不知道怎么插入链接qwq就只好粘个文本的了qwq:https://www.luogu.org/problemnew/show/P1458 没错,是个黄题,因为你们的小蒟蒻只会这样 ...
- 【字符串】 manacher算法
Algorithm Task 给定一个字符串,求其最长回文子串 Limitations 要求时空复杂度均为线性且与字符集大小无关. Solution 考虑枚举回文串的对称轴,将其对应的最长回文子串长度 ...
- DAVID 进行 GO/KEGG 功能富集分析
何为功能富集分析? 功能富集分析是将基因或者蛋白列表分成多个部分,即将一堆基因进行分类,而这里的分类标准往往是按照基因的功能来限定的.换句话说,就是把一个基因列表中,具有相似功能的基因放到一起,并和生 ...
- tcp & 长连接 短连接
参考文档: tcp协议 http://blog.chinaunix.net/uid-26833883-id-3627644.html 长连接和短连接 http://blog.csdn.net/free ...
- 配置tomcat服务器内存大小中的Xms、Xmx、PermSize、MaxPermSize 详解
1.参数的含义 -vmargs -Xms256m -Xmx512m -XX:PermSize=256M -XX:MaxPermSize=512M -vmargs 说明后面是VM的参数,所以后面的其实都 ...
- Docker安装及简单使用(Ubuntu)
### Ubuntu18下docker安装 * step 1: 安装必要的一些系统工具 sudo apt-get update sudo apt-get -y install apt-transpor ...
- kubernetes学习一:安装及部署第一个Web应用
准备工作 首先准备Kubernets的环境,使用的是centos7.5 关闭防火墙: # systemctl disable firewalld # systemctl stop firewalld ...
- fp-growth代码问题(Python)
网上的 python3 fp-growth代码每次在执行时可能会出现找出的频繁项集不一致的情况,这是因为每次执行代码时建的FP树可能不一致. 加了一行代码可以解决这个问题(第59行):先对 frequ ...