原文地址:http://blog.csdn.net/aiuyjerry/article/details/8595991

Storage模块主要负责数据存取,包括MapReduce Shuffle中间结果、MapReduce task中间stage结果、cache结果。下面从架构和源码细节上来分析Storage模块的实现。Storage模块主要由两大部分组成:

  • BlockManager部分主要负责Master和Slave之间的block通信,主要包括BlockManager状态上报、心跳,add, remove, update block.
  • BlockStore部分主要负责数据存取,Spark根据不同选择可以在Memory或(和)Disk中存储序列化数据.
Storage模块类图如下所示:
       
  • SparkEnv创建时会实例化BlockManagerMaster对象和BlockManager对象。
  • BlockManagerMaster对象会根据自己是master还是slave来创建BlockManagerMasterActor或是连接到BlockManagerMasterActor。
  • BlockManager承担两种角色:
    1. 负责向BlockManagerMaster上报block信息,保持心跳和接收block信息
    2. 负责通过BlockStore从Memory或Disk读取、写入block数据
  • BlockManagerMessages封装与master传输的meta信息的具体格式。
  • Slave通过BlockManager向BlockManagerMaster注册自己,在注册自己时会创建BlockManagerSlaveActor,用来Master向Slave通信,目前唯一request是请求Slave删除block。
  • BlockManagerWorker则负责Slave之间的通信,包括get, put非本地的block
  • BlockMessage类封装了与Master通信的block message的具体格式,而BlockMessageArray则是批处理接口。
  • BlockStore提供持久化数据的接口,DiskStore和MemoryStore实例化了BlockStore接口,实现serialize, deserialize数据到Disk或Memory。
 
Spark Storage模块master和slave之间通信的信息包括:
  • Slave -------->  Master
    • RegisterBlockManager
    • HeartBeat
    • UpdateBlockInfo
    • GetLocations
    • GetLocationsMutipleBlockIds
    • GetPeers
    • RemoveExecutor
    • StopBlockManagerMaster
    • GetMemoryStatus
    • ExpireDeadHosts
    • GetStorageStatus
  • Master ---------> Slave
    • RemoveBlock
 
Storage模块存取数据分析
MemoryStore:
    Memory内部使用LinkedHashMap来作为block的存储结构,其中key是block id,value是Entry类,代码如所示:
  case class Entry(value: Any, size: Long, deserialized: Boolean, var dropPending: Boolean = false)
private val entries = new LinkedHashMap[String, Entry](32, 0.75f, true)
而内部存储会调用如下代码:
  private def tryToPut(blockId: String, value: Any, size: Long, deserialized: Boolean): Boolean = {
putLock.synchronized {
if (ensureFreeSpace(blockId, size)) {
val entry = new Entry(value, size, deserialized)
entries.synchronized { entries.put(blockId, entry) }
currentMemory += size
if (deserialized) {
logInfo("Block %s stored as values to memory (estimated size %s, free %s)".format(
blockId, Utils.memoryBytesToString(size), Utils.memoryBytesToString(freeMemory)))
} else {
logInfo("Block %s stored as bytes to memory (size %s, free %s)".format(
blockId, Utils.memoryBytesToString(size), Utils.memoryBytesToString(freeMemory)))
}
true
} else {
// Tell the block manager that we couldn't put it in memory so that it can drop it to
// disk if the block allows disk storage.
val data = if (deserialized) {
Left(value.asInstanceOf[ArrayBuffer[Any]])
} else {
Right(value.asInstanceOf[ByteBuffer].duplicate())
}
blockManager.dropFromMemory(blockId, data)
false
}
}
} private def ensureFreeSpace(blockIdToAdd: String, space: Long): Boolean = {
logInfo("ensureFreeSpace(%d) called with curMem=%d, maxMem=%d".format(
space, currentMemory, maxMemory)) if (space > maxMemory) {
logInfo("Will not store " + blockIdToAdd + " as it is larger than our memory limit")
return false
} if (maxMemory - currentMemory < space) {
val rddToAdd = getRddId(blockIdToAdd)
val selectedBlocks = new ArrayBuffer[String]()
var selectedMemory = 0L entries.synchronized {
val iterator = entries.entrySet().iterator()
while (maxMemory - (currentMemory - selectedMemory) < space && iterator.hasNext) {
val pair = iterator.next()
val blockId = pair.getKey
if (rddToAdd != null && rddToAdd == getRddId(blockId)) {
logInfo("Will not store " + blockIdToAdd + " as it would require dropping another " +
"block from the same RDD")
return false
}
selectedBlocks += blockId
selectedMemory += pair.getValue.size
}
} if (maxMemory - (currentMemory - selectedMemory) >= space) {
logInfo(selectedBlocks.size + " blocks selected for dropping")
for (blockId <- selectedBlocks) {
val entry = entries.synchronized { entries.get(blockId) }
// This should never be null as only one thread should be dropping
// blocks and removing entries. However the check is still here for
// future safety.
if (entry != null) {
val data = if (entry.deserialized) {
Left(entry.value.asInstanceOf[ArrayBuffer[Any]])
} else {
Right(entry.value.asInstanceOf[ByteBuffer].duplicate())
}
blockManager.dropFromMemory(blockId, data)
}
}
return true
} else {
return false
}
}
return true
}
    tryToPut会调用ensureFreeSpace来淘汰掉一些block,为此block的存储释放新的空间,而tryToPut会将其添加到LinkedHashMap中。如果ensureFreeSpace无法获得足够的空间去存储此block,tryToPut会调用dropFreeMemory来drop此block。
 
DiskStore:
   Spark会根据配置项spark.local.dir在本地建立目录,所有的block都会依照不同路径存储到此目录下,当spark.local.dir中配置了多个path时,Spark会根据hash将block存储到不同的path下
  • 首先,Spark会根据spark.local.dir的配置在所有配置目录下建立localDir,localDir命名为spark-local-%s-%04x,其中%s是格式化后的当前时间(yyyyMMddHHmmss),%d是一个小于65535的随机16进制数字。
  • 其次,每当要存储block时,Spark会根据blockId在localDir下建立子目录和相应的文件,block存储目录的选择规律是:
    1. 根据blockId的hash值计算出dirId和subDirId
    2. 取出或创建subDir
    3. 在subDir下面以blockId为名字创建文件
    val subDirsPerLocalDir = System.getProperty("spark.diskStore.subDirectories", "64").toInt
val subDirs = Array.fill(localDirs.length)(new Array[File](subDirsPerLocalDir)) // Figure out which local directory it hashes to, and which subdirectory in that
val hash = math.abs(blockId.hashCode)
val dirId = hash % localDirs.length
val subDirId = (hash / localDirs.length) % subDirsPerLocalDir // Create the subdirectory if it doesn't already exist
var subDir = subDirs(dirId)(subDirId)
if (subDir == null) {
subDir = subDirs(dirId).synchronized {
val old = subDirs(dirId)(subDirId)
if (old != null) {
old
} else {
val newDir = new File(localDirs(dirId), "%02x".format(subDirId))
newDir.mkdir()
subDirs(dirId)(subDirId) = newDir
newDir
}
}
} new File(subDir, blockId)
  • 最后,根据压缩和序列化方式选择将block存储到文件中

【转】Spark源码分析之-Storage模块的更多相关文章

  1. Spark源码分析之-Storage模块

    原文链接:http://jerryshao.me/architecture/2013/10/08/spark-storage-module-analysis/ Background 前段时间琐事颇多, ...

  2. 【转】Spark源码分析之-deploy模块

    原文地址:http://jerryshao.me/architecture/2013/04/30/Spark%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8B- ...

  3. 【转】Spark源码分析之-scheduler模块

    原文地址:http://jerryshao.me/architecture/2013/04/21/Spark%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8B- ...

  4. Spark源码分析 – BlockManager

    参考, Spark源码分析之-Storage模块 对于storage, 为何Spark需要storage模块?为了cache RDD Spark的特点就是可以将RDD cache在memory或dis ...

  5. Spark源码分析 – 汇总索引

    http://jerryshao.me/categories.html#architecture-ref http://blog.csdn.net/pelick/article/details/172 ...

  6. Spark源码分析 – Deploy

    参考, Spark源码分析之-deploy模块   Client Client在SparkDeploySchedulerBackend被start的时候, 被创建, 代表一个application和s ...

  7. Spark源码分析 – SparkContext

    Spark源码分析之-scheduler模块 这位写的非常好, 让我对Spark的源码分析, 变的轻松了许多 这里自己再梳理一遍 先看一个简单的spark操作, val sc = new SparkC ...

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

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

  9. spark 源码分析之十五 -- Spark内存管理剖析

    本篇文章主要剖析Spark的内存管理体系. 在上篇文章 spark 源码分析之十四 -- broadcast 是如何实现的?中对存储相关的内容没有做过多的剖析,下面计划先剖析Spark的内存机制,进而 ...

随机推荐

  1. VUE(现代库) VS jquery(传统库)

      众所周知最近几年前端发展非常的迅猛,除各种框架如:backbone.angular.reactjs外,还有模块化开发思想的实现库:sea.js .require.js .webpack以及 前端上 ...

  2. 小强 ROS 机器人教程

    首先请您自行依据线标提示将小强接线连接好,完整结构如下两图所示: 小强是属于Turtlebot机器人.它由底盘.主机.Kinect相机(通过USB连接主机)组成,没有显示屏.如果要通过显示器查看主机的 ...

  3. 2013多校联合3 G The Unsolvable Problem(hdu 4627)

    2013-07-30 20:35 388人阅读 评论(0) 收藏 举报 http://acm.hdu.edu.cn/showproblem.php?pid=4627 The Unsolvable Pr ...

  4. 在ANTMINER(阉割版BeagleBone Black)运行Debain

    开门见山,直入主题 咸鱼入手3块阉割ARM板,经过快递近6天运输到手,不过价格便宜 东西下面这样的(借了咸鱼的图): 发现这块板是阉割版的国外beagleboard.org型号为BeagleBone ...

  5. 配置Jenkins构建失败触发邮件报警机制

    系统管理 1.进入系统管理-->系统设置 定位到Jenkins Location配置项   配置系统管理员邮件地址 系统管理员邮件地址需要同发送报警邮件地址相同 定位到邮件通知   配置SMTP ...

  6. C#基础复习(2) 之 装箱拆箱

    参考资料 [1] @只增笑耳Jason的回答 https://www.zhihu.com/question/57208269 [2] <C# 捷径教程> 疑难解答 装箱和拆箱是什么? 何时 ...

  7. RTOS双向链表数据结构

    在学习RTOS操作系统时,在任务优先级设置时用到了双向链表,说实话数据结构的东西只是停留在大学上课阶段,并未实践过,在操作系统中看得云里雾里,遂将其单独拿来了进行了一下思考,经过一个上午的摸索逐渐领会 ...

  8. c# 调用短信平台接口,给手机发送短信

    项目上要做个发手机短信的功能.网上找找了,用的微米的短信接口. 注册后,获得UID和UID key,C#代码中需要这个 调用代码很简单 ", con = "[微米]您的验证码是:6 ...

  9. log4j学习(二)不同类的日志输出到不同的文件

    目的:一个应用中有两个不同作用的后台服务,我们需要把他们的日志分开,存放到2个不同的日志文件中. 办法:需要在log4j.properties文件中配置两个不同的logger和对应的appender ...

  10. 纯文本-FileInputStream的编码与解码方式

    前言:以下分析只针对纯文本 1.FileInputStream默认的编码方式就是文件的编码方式 即:源文件是什么编码方式,则利用FileInputStream默认读取的字节数组,就是什么编码方式. 例 ...