之前分析了spark任务提交以及计算的流程,本文将分析在计算过程中数据的读写过程。我们知道:spark抽象出了RDD,在物理上RDD通常由多个Partition组成,一个partition对应一个block。在driver和每个executor端,都有一个Blockmanager。Blockmanager是spark在计算过程中对block进行读写的入口,它屏蔽了在读取数据时涉及到的内存分配,从其他executor端远程获取等具体细节。接下来,本文将以读写block为主线,分析spark在计算过程中读写实际数据的流程。

1,计算数据写流程

1.1,从计算上来说, RDD中的一个Partition对应一个Task。在Task在taskRunner的run方法中调用task.run方法,然后根据计算结果的大小,以不同形式(直接发送或者通过blockManager)将数据发送给driver。

val value = try {
val res = task.run(
taskAttemptId = taskId,
attemptNumber = attemptNumber,
metricsSystem = env.metricsSystem)
threwException = false
res
}

...

// directSend = sending directly back to the driver
val serializedResult: ByteBuffer = {
if (maxResultSize > 0 && resultSize > maxResultSize) {
logWarning(s"Finished $taskName (TID $taskId). Result is larger than maxResultSize " +
s"(${Utils.bytesToString(resultSize)} > ${Utils.bytesToString(maxResultSize)}), " +
s"dropping it.")
ser.serialize(new IndirectTaskResult[Any](TaskResultBlockId(taskId), resultSize))
} else if (resultSize > maxDirectResultSize) {
val blockId = TaskResultBlockId(taskId)
env.blockManager.putBytes(
blockId,
new ChunkedByteBuffer(serializedDirectResult.duplicate()),
StorageLevel.MEMORY_AND_DISK_SER)
logInfo(
s"Finished $taskName (TID $taskId). $resultSize bytes result sent via BlockManager)")
ser.serialize(new IndirectTaskResult[Any](blockId, resultSize))
} else {
logInfo(s"Finished $taskName (TID $taskId). $resultSize bytes result sent to driver")
serializedDirectResult
}
}

1.2,在使用block形式的时候,可以看到调用了blockManager的putBytes方法,在核心实现doPutBytes中,根据存储级别和是否序列化使用memstore和diskstore中不同的不方法进行存储。顾名思义,memstore和diskstore主要就是根据存储级别将对应的block储存到内存或者磁盘。

1.3,memstore的putBytes实现如下。可以看到首先需要通过memoryManager申请存储保存当前block的内存,申请到内存后改block的数据,会以BlockId和SerializeMemoryEntry的键值对,保存在memstore的entries的对象中。关于memoryManager,当前有StaticMemoryManager和UnifiedMemoryManager两种实现。StaticMemoryManager是之前老的实现,将spark计算过程使用的存储内存和计算内存按照总大小的固定比例进行分配。UnifiedMemoryManager是2.x的默认实现,相对StaticMemoryManager,UnifiedMemoryManager中存储和计算的内存是可以动态调整的。也就是说,当计算内存紧张,储存内存空闲的时候,计算内存可以借用存储内存。反之类似。

1.4,在1.3完成当前executor机器完成当前block存储以后,当需要告诉driver时(tellMaster参数),会将该block的状态汇报给driver(reportBlockStatus),通过向dirver发送UpdateBlockInfo消息。driver接收到UpdateBlockInfo消息后,将汇报过来的相关信息保存在BlockManagerMasterEndpoint的blockManagerInfo和blockLocations中。

至此,计算过程写数据的流程完成。

2,计算数据读流程

2.1,话说在TaskRunner运行结束以后,会调用execBackend.statusUpdate,会将该任务的结束的状态通过StatusUpdate的信息发送给driver。

2.2,driver端接收到StatusUpdate消息后,最终将调用TaskResultGetter的enqueueSuccessfulTask方法。在该方法中,对于使用block(即IndirectTaskResult),最终将调用blockManager的getRemoteBytes获取该blockId对应的数据。

2.3,在blockManager的getRemoteBytes方法中,主要逻辑是获取该blockId对应的存储该blockId数据的所有机器位置,通过调用blockTransferService的fetchBlockSync获取具体数据,一旦从一个指定的位置获取到数据,则立即返回。

2.4,fetchBlockSync接着会调用具体实现NettyBlockTransferService中的fetchBlocks方法,在该方法中,将通过OneForOneBlockFetcher发送OpenBlocks消息给指定目标的blockManager,从而对应的streamId等信息,然后通过

client.fetchChunk一次获取每块的数据。

2.5,在提供数据的blockManager端(即server端),接受到消息OpenBlocks消息后,首先根据blockId通过blockManager的getBlockData方法获取对应的数据,然后将该数据和一个streamId奖励对应关系(通过streamManager调用进行)

2.6,在2.4中获取到对应的streamId后,将通过ChunkFetchRequest分块获取数据。server端接受到该消息以后,streamManager将根据streamId和chunkIndex获取对应数据,然后返回给客户端。

至此,计算过程获取block数据的流程结束。

spark block读写流程分析的更多相关文章

  1. S3C6410 SPI全双工读写流程分析(原创)【转】

    转自:http://blog.csdn.net/hustyangju/article/details/21165721 原创博文,知识共享!转载请注明出处:http://blog.csdn.net/h ...

  2. Spark修炼之道(高级篇)——Spark源代码阅读:第十二节 Spark SQL 处理流程分析

    作者:周志湖 以下的代码演示了通过Case Class进行表Schema定义的样例: // sc is an existing SparkContext. val sqlContext = new o ...

  3. hbase读写流程分析

    前言 最近被大佬问到一个问题,hbase查询数据在最坏的场景下需要进行几次rpc,当时就懵了..下面主要对client端代码进行分析.阅读文章和看源码更配~ 读数据 流程总览 1. 从zookeepe ...

  4. spark 启动job的流程分析

    从WordCount開始分析 编写一个样例程序 编写一个从HDFS中读取并计算wordcount的样例程序: packageorg.apache.spark.examples importorg.ap ...

  5. 第一篇:Spark SQL源码分析之核心流程

    /** Spark SQL源码分析系列文章*/ 自从去年Spark Submit 2013 Michael Armbrust分享了他的Catalyst,到至今1年多了,Spark SQL的贡献者从几人 ...

  6. 10.Spark Streaming源码分析:Receiver数据接收全过程详解

    原创文章,转载请注明:转载自 听风居士博客(http://www.cnblogs.com/zhouyf/)   在上一篇中介绍了Receiver的整体架构和设计原理,本篇内容主要介绍Receiver在 ...

  7. 【转】HDFS读写流程

    概述开始之前先看看其基本属性,HDFS(Hadoop Distributed File System)是GFS的开源实现. 特点如下: 能够运行在廉价机器上,硬件出错常态,需要具备高容错性流式数据访问 ...

  8. HDFS读写流程(转载)

    概述开始之前先看看其基本属性,HDFS(Hadoop Distributed File System)是GFS的开源实现.特点如下:    能够运行在廉价机器上,硬件出错常态,需要具备高容错性    ...

  9. 如何在spark中读写cassandra数据 ---- 分布式计算框架spark学习之六

    由于预处理的数据都存储在cassandra里面,所以想要用spark进行数据分析的话,需要读取cassandra数据,并把分析结果也一并存回到cassandra:因此需要研究一下spark如何读写ca ...

随机推荐

  1. storage engine option for directoryPerDB

    Requested option conflicts with current storage engine option for directoryPerDB; you requested true ...

  2. Hibernate exception

    1.a different object with the same identifier value was already associated with the session. 错误原因:在h ...

  3. SpringMvc參数的接受以及serializeArray的使用方法

    需求:从页面提交一个table中的数据到后台,通经常使用于批量改动 把全部的数据到放到 input属性中,设置name定义成为对象的相关属性,使用Jquery的serializeArray这种方法封装 ...

  4. 单字节的FIFO缓存(30天自制操作系统--读书笔记)

    从今天起,写一些读书笔记.最近几个月都在看<30天自制操作系统这本书>,书虽说看的是电子书,但可以花钱买的正版书,既然花费了金钱,就总得有些收获. 任何人都不能总是固步自封,想要进步就得学 ...

  5. curl请求接口返回false,错误码60

    我讲一下我遇到的这个问题,是因为最近服务器加了https导致的,网上找到了答案,加上这句 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 就可以正常返 ...

  6. thinkphp 防sql注入

    $Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->selec ...

  7. GNU Makeflie

    简介 Gnu Make主要用于构建和管理程序包.Makefile文件描述了整个工程的编译.连接等规则. 其中包括: 工程中的哪些源文件需要编译以及如何编译: 需要创建那些库文件以及如何创建这些库文件: ...

  8. 代码块、继承、this、super、final(java基础知识八)

    1.代码块的概述和分类 * A:代码块概述 * 在Java中,使用{}括起来的代码被称为代码块.* B:代码块分类 * 根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块( ...

  9. jdk8新特性Stream

    Stream的方法描述与实例 1,filter  过滤 Person p1 = new Person(); p1.setName("P1"); p1.setAge(10); Per ...

  10. UVA 11174 Stand in a Line 树上计数

    UVA 11174 考虑每个人(t)的所有子女,在全排列中,t可以和他的任意子女交换位置构成新的排列,所以全排列n!/所有人的子女数连乘   即是答案 当然由于有MOD 要求逆. #include & ...