Storage模块详解
来源:互联网 发布:菜刀连接php图片木马 编辑:程序博客网 时间:2024/05/19 22:27
8.1 模块整体架构
Storage模块采用Master/Slave架构,Master负责整个Application的Block元数据信息的管理和维护;Slave负责将Block的更新等状态上报到Master,Master和Slave之间通过AKKA消息传递机制通信,Master与Slave之间并没有心跳检测而是间接通过Driver与Executor之间的心跳检测而来
Master的元数据信息管理与维护主要保存在BlockManagerMasterActor的三个数据结构中:
1)private valblockManagerInfo = new mutable.HashMap[BlockManagerId,BlockManagerInfo],保存BlockManagerId->BlockManagerInfo的映射,BlockManagerInfo保存Slave节点的内存使用情况、Block的状态、BlockManagerSlaveActor的Reference
2)private valblockManagerIdByExecutor=new mutable.HashMap[String,BlockManagerId]保存ExecutorID->BlockManagerId的映射
3)private valblockLocations=new JHashMap[BlockId,mutable.HashSet[BlockManagerId]],保存Block在哪些BlockManager上的HashMap,通过查询可知blockLocations可以找到某个Block所在的物理位置(HashSet说明该Block可能存在多个副本)
Slave的BlockManager创建后,需要向BlockManagerMasterActor发送RegisterBlockManager进行注册,master.registerBlockManager(blockManagerId,maxMemory,slaveActor)//参数分别为BlockManager标识,节点最大使用内存数,BlockManagerSlaveActor,利用blockManagerIdByExecutor把Slave信息保存在Master端
Driver端的实现:
SparkContext->SparkEnv->BlockManager->BlockManagerMaster转发请求至->BlockManagerMasterActor完成元数据管理和维护
Executor端的实现:
BlockManager->BlockManagerMaster持有Driver端的BlockManagerMasterActor的Reference,以此来完成Slave到Master的交互;除此之外BlockManager还拥有BlockManagerSlaveActor即该Actor的Reference会被上传到Master,Master会利用此Reference向Slave发送一些命令,比如删除Slave上的RDD、Shuffle相关的数据或者广播变量
8.1.2 源码组织结构
BlockManager提供读写Block的接口,BlockID格式是rdd_rddId_partitionId,其中Master端的BlockManager负责整个Application的Block管理工作;运行在Executor端的BlockManager负责管理该Executor上的Block,并向Driver的BlockManager汇报Block信息和接收它的命令
DiskBlockManager:管理及维护逻辑Block->物理Block的映射,根据BlockId->物理文件->hash到spark.local.dir设置的目录中
BlockStore有三种实现分别为DiskStore(Block存入Disk,写Disk是由DiskBlockObjectWriter),MemoryStore(Block存入Memory),TachyonStore(Block存入Tachyon)
TachyonBlockManager:逻辑Block->Tachyon文件系统的映射
8.1.3 Master和Slave的消息传递详解
1)Master->Slave的消息详解(以删除RDD为例的调用栈)
Ø SparkContext.unpersist(id,blocking)
Ø BlockManagerMaster.removeRdd(rddId,blocking)
Ø BlockManagerMaster.askDriverWithReply[Future[Seq[Int]]](RemoveRdd(rddId))
Ø AkkaUtils.askWithReply(message,driverActor,AKKA_RETRY_ATTEMPTS,AKKA_RETRY_INTERVAL_MS,timeout)
Ø BlockManagerMasterActor.receiveWithLogging
case RemoveRdd(rddId)=>sender!removeRdd(rddId)
Ø BlockManagerMasterActor.removeRdd(rddId)首先删除RDD的元数据信息,然后删除Slave的RDD信息
删除RDD的元数据信息
valblocks=blockLocations.keys.flatMap(_.asRDDId).filter(_.rddId==rddId)//根据rddId过滤得到待删除的blocks信息
Blocks.foreach{blockId=>valbms:mutable.HashSet[BlockManagerId]=blockLocations.get(blockId)}//将blockId信息映射为BlockManagerId
Bms.foreach(bm=>blockManagerInfo.get(bm).foreach(_.removeBlock(blockId)))//BlockManagerId->BlockManagerInfo,从blockManagerInfo中删除blockId对应的元数据信息
blockLocations.remove(blockId)//从blockLocations中删除blockId对应的元数据
其次删除Slave上的RDD信息
Ø Master端向Slave发送删除Block命令
val removeMsg=RemoveRdd(rddId)
Future.sequence(
blockManagerInfo.values.map{bm=>
bm.slaveActor.ask(removeMsg)(akkaTimeout).mapTo[Int] //遍历blockManagerInfo,每个blockManagerInfo.slaveActor.ask(removeMsg)即向其slaveActor发送removeMsg命令
}.toSeq
Ø Slave接收到RemoveRdd的消息,调用BlockManager删除RDD
Case RemoveRdd(rddId)=>
doAsync[Int](“removing RDD”+rddId,sender){
blockManager.removeRdd(rddId)//BlockManager根据rddId删除RDD数据
}
BlockManager端的实现:
def removeRdd(rddId:Int):Int={
valblocksToRemove=blockInfo.keys.flatMap(_.asRDDId).filter(_.rddId=rddId)
blocksToRemove.foreach{
blockId=>removeBlock(blockId,tellMaster=false)//删除BlockId对应的Block
}
blocksToRemove.size
}
2)Slave->Master的消息详解
Slave向Master汇报某个Block的状态更新BlockManager.reportBlockStatus()的调用栈信息:
Ø BlockManager.tryToReportBlockStatus
valneedRegister=!tryToReportBlockStatus(blockId,info,status,droppedMemorySize)
Ø BlockManagerMaster.updateBlockInfo
master.updateBlockInfo(blockManagerId,blockId,storageLevel,inMemSize,onDiskSize,inTachyonSize)
Ø BlockManagerMaster.askDriverWithReply
valres=askDriverWithReply[Boolean](UpdateBlockInfo(blockManagerId,blockId,storageLevel,memSize,diskSize,tachyonSize))
Ø AkkaUtils.askWithReply(message,driverActor,AKKA_RETRY_ATTEMPTS,AKKA_RETRY_INTERVAL_MS,timeout)
Ø BlockManagerMasterActor.receiveWithLogging()
case UpdateBlockInfo(
blockManagerId,blockId,storageLevel,deserializedSize,size,tachyonSize)=>
sender!updateBlockInfo(
blockManagerId,blockId,storageLevel,deserializedSize,size,tachyonSize
)
Ø BlockManagerMasterActor.updateBlockInfo
blockManagerInfo(blockManagerId).updateBlockInfo(
blockId,storageLevel,memSize,diskSize,tachyonSize
)
Ø locations=blockLocations.get(blockId) //根据blockId得到其物理位置信息
if(storageLevel.isValid){
locations.add(blockManagerId) //为该Block加入新的位置
}
else{
locations.remove(blockManagerId)//删除无效的Block的位置
}
if(locations.size==0){
blockLocations.remove(blockId)
} //如果locations的大小为0啦,说明Slave上再无此Block,直接根据blockId删除blockLocations记录
8.2 存储实现详解
8.2.1 存储级别
StorageLevel提供了多种存储策略包括MEMORY_ONLY,DISK_ONLY,MEMORY_AND_DISK等等
存储级别的选择:
1)默认的是MEMORY_ONLY即RDD的partition存储在内存中,多余的直接丢弃
2)减少内存使用则使用MEMORY_ONLY_SER
3)尽量不要落在硬盘,除非中间结果计算逻辑复杂
4)容错则选择MEMORY_ONLY_2
5)如果集群中有大量内存或者运行任务,则选择OFF_HEAP,多个Executor共享一个内存池,减少GC开销,缓存数据即使其Executor异常退出了,也不会丢失
8.2.2 模块类图
private abstract class BlockStore(valblockManager:BlockManager)extends Logging{
def putBytes(blockId,bytes,level):PutResult //根据StorageLevel将blockId标识的Block内容bytes写入系统
defputIterator(blockId,values,level,returnValues):PutResult //将values:Iterator[Any]写入系统
def putArray(blockId,values,level,returnValues):PutResult//将values:Array[Any]写入系统
8.2.3 DiskStore
DiskBlockManager管理文件,即逻辑Block->物理文件file->hash映射到spark.local.dir目录中
DiskBlockManager会为Executor在每个目录下创建一个子目录,子目录的命名方式是”spark-local-yyyyMMddHHmmss-xxxx”,在此目录下又可以生成至多spark.diskStore.subDirectories(默认值是64)的子目录,DiskBlockManager采用两段Hash定位到Block实际存放地址
8.2.4 MemoryStore
实际上通过HashMap管理Block数据
private val entries=newLinkedHashMap[BlockId,MemoryEntry](32m0.75f,true)
对外提供的写入接口通过MemoryStore.tryToPut,剩余内存容量检查(MemoryStore.ensureFreeSpace),内存超过最大内存则会立即返回调用者,如果存储级别还有Disk,则将当前数据通过DiskStore写入Disk,否则缓存不会被持久化
8.2.5 TachyonStore
Tachyon文件的读取和写入通过TachyonBlockManager完成,TachyonStore.getBytes(blockId:BlockId)与BlockStore类似,也会生成spark-tachyon-yyyyMMddHHmmss-xxxx目录,以及spark.tachyonStore.subDirectories(默认64)的子目录
8.2.6 Block存储的实现
CacheManager判断结果是否已缓存,如果是则直接读取缓存,否则开始计算
Ø SparkEnv.get.CacheManager.getOrCompute(this,split,context,storageLevel)
Ø val computedValues=rdd.computeOrReadCheckpoint(partition,context)//计算RDD或者读Checkpoint
Ø blockManager.putArray(key,arr,level,telllMaster=true,effectiveStorageLevel)//计算的记过或写入缓存,返回值为PutResult,effectiveStorageLevel决定了结果最终写到哪,若tellMaster=true则需要reportBlockStatus
8.3 性能调优
8.3.1spark.local.dir
尽量在较快存储设备上配置更多的目录来增加其被使用的比例
8.3.2spark.executor.memory
Executor占用的内存
8.3.3 spark.storage.memoryFraction默认值是0.6,决定内存中有多少可以用于MemoryStore管理RDD Cache数据,多少内存用来满足任务运行时各种其他内存空间的需要
如果频繁发生GC,可以考虑降低此比例
8.3.4 Spark.streaming.blockInterval
设置Spark Streaming里Stream Receiver生成Block的时间间隔,默认是200ms- Storage模块详解
- Spark之 Data storage 模块
- spark-storage模块源码分析
- spark-Storage 模块整体架构
- FastDFS Storage配置详解
- FastDFS Storage配置详解
- FastDFS Storage配置详解
- Spark源码解析——Storage模块
- Spark源码分析之-Storage模块
- Spark技术内幕:Storage 模块整体架构
- Spark技术内幕:Storage 模块整体架构
- Spark技术内幕:Storage 模块整体架构
- Spark技术内幕:Storage 模块整体架构
- Spark源码分析之-Storage模块
- Shuffle 与 Storage 模块间的交互
- Tracker与Storage配置详解
- Storage
- FastDFS配置文件详解之storage.conf
- Python多进程并发(multiprocessing)
- Android--------使用BottomTabBar实现底部导航页
- chapter5 行列式
- 阻塞/非阻塞 I/O 同步与异步
- 【微营销】第13天 微营销的落地执行(一)
- Storage模块详解
- Xilinx ISE 14.7与Modelsim10.1a联合仿真
- Kuwahara filter (python版)
- webrtc 开启flexfec
- runtime
- MyEclipse部署Tomcat服务器时总是跳到Debug模式
- IDEA 连接sql service
- function2--闭包
- 松弛操作的性质