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的更多相关文章

  1. Spark存储管理(读书笔记)

    Spark存储管理(读书笔记) 转载请注明出处:http://www.cnblogs.com/BYRans/ Spark的存储管理 RDD的存放和管理都是由Spark的存储管理模块实现和管理的.本文从 ...

  2. Spark 概念学习系列之Spark存储管理机制

    Spark存储管理机制 概要 01 存储管理概述 02 RDD持久化 03 Shuffle数据存储 04 广播变量与累加器 01 存储管理概述 思考: RDD,我们可以直接使用而无须关心它的实现细节, ...

  3. spark 存储管理机制

    累加器 -- Accumulators 广播变量--Broadcast Variables 思考 回顾 存储管理模块架构--从架构上来看 存储管理模块架构--通信层 存储管理模块架构--存储层 存储管 ...

  4. Spark源码阅读之存储体系--存储体系概述与shuffle服务

    一.概述 根据<深入理解Spark:核心思想与源码分析>一书,结合最新的spark源代码master分支进行源码阅读,对新版本的代码加上自己的一些理解,如有错误,希望指出. 1.块管理器B ...

  5. Spark源码剖析 - SparkContext的初始化(八)_初始化管理器BlockManager

    8.初始化管理器BlockManager 无论是Spark的初始化阶段还是任务提交.执行阶段,始终离不开存储体系.Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈,优先将配置信息.计算结 ...

  6. Spark源码分析之九:内存管理模型

    Spark是现在很流行的一个基于内存的分布式计算框架,既然是基于内存,那么自然而然的,内存的管理就是Spark存储管理的重中之重了.那么,Spark究竟采用什么样的内存管理模型呢?本文就为大家揭开Sp ...

  7. Spark学习笔记--概念知识

    RDD被视为由不同的数据块组成,对于RDD的存取是以数据块为单位的,本质上分区(partition)和数据块(block)是等价的,只是看待的角度不同. 数据块 Spark存储管理模块中所管理的几种主 ...

  8. Spark分布式编程之全局变量专题【共享变量】

    转载自:http://www.aboutyun.com/thread-19652-1-1.html 问题导读 1.spark共享变量的作用是什么?2.什么情况下使用共享变量?3.如何在程序中使用共享变 ...

  9. spark总结——转载

    转载自:    spark总结 第一个Spark程序 /** * 功能:用spark实现的单词计数程序 * 环境:spark 1.6.1, scala 2.10.4 */ // 导入相关类库impor ...

随机推荐

  1. IDEA+SpringBoot项目启动参数设置

    SpringBoot属性加载顺序 顺序 形式 1 在命令行中传入的参数 2 SPRING_APPLICATION_JSON中的属性.SPRING_APPLICATION_JSON是以JSON的格式配置 ...

  2. [考试]NOIP2015模拟题2

    // 此博文为迁移而来,写于2015年7月22日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w72i.html 1.总 ...

  3. PATA1012The Best Rank(25分)

    To evaluate the performance of our first year CS majored students, we consider their grades of three ...

  4. PATA1077Kuchiguse

    需要注意的有关于二维字符串数组的输入问题,先是定义要多留一位用于存放'\0' 还有就是使用scanf后,会有回车换行符,如果要使用gets或是接下来的方式代替gets,记得加上getchar,不然会出 ...

  5. C# 动态加载(转)

    原文链接地址:http://blog.csdn.net/lanruoshui/article/details/5090710 原理如下: 1.利用反射进行动态加载和调用. Assembly assem ...

  6. LDoc使用总结

    LDoc使用总结 安装 按照下面的安装就可以了 http://www.cnblogs.com/ZhYQ-Note/articles/6022525.html 使用 参考:官方的说明文档 https:/ ...

  7. 配置MySQL主从复制和读写分离

    实验环境 序号 主机名 IP地址 备注 1 mysql-master 192.168.204.201 MySQL主库 2 mysql-slave 192.168.204.202 MySQL从库 3 a ...

  8. c++篇 cad.grx 入门,手动配置编译环境

    安装vs2010+sp1补丁; 安装浩辰2018(64位版本); 下载浩辰Grx开发的SDK,注意对应版本年份., 解压到E盘目录下, E:\grxsdk 在他们的官方用户群下载,搜sdk, 找到gr ...

  9. Postman接口测试【3】_自动添加随笔

    一.抓取博客园编写博客地址 1.通过Charles抓取,获取到编写博客接口地址和接口的参数 二.Postman请求接口 打开Postman,输入上面抓到的接口地址,接口类型为POST,请求参数为x-w ...

  10. Adobe Audition cc 修改音频 --- 淡出、淡入,合并、裁剪

    ​1.导入音频到Adobe Audition cc中支持多个音频操作 也可以使用鼠标把音频放入其中 2.淡出淡入: 红色圈中的就是淡出淡入的控制符 左手按住ctrl 或者 shift 键  右手按住鼠 ...