之前分析了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. tomcat 部署项目的多种方式

    项目放在tomcat webapps也不会加载两次 下面可以指定项目名称及path   加载war   部署war包  后面不用加war的后缀 <Host appBase="D:/pr ...

  2. KMP 、扩展KMP、Manacher算法 总结

    一. KMP 1 找字符串x是否存在于y串中,或者存在了几次 HDU1711 Number Sequence HDU1686 Oulipo HDU2087 剪花布条 2.求多个字符串的最长公共子串 P ...

  3. oracle监听器启动,实例启动

    1,su - oracle登录oracle用户 2,sqlplus / as sysdba登录oracle 3,show parameter service_names; 若正常输出servicena ...

  4. Java虚拟机一览表

    免费和开源的 Java 虚拟机AegisVM (inactive since 2004).Apache Harmony — supports several architectures and sys ...

  5. Oracle的wm_concat函数(转)

    wm_concat函数简单说就是行转列显示. 转自:http://christy-fang.iteye.com/blog/1299850 首先让我们来看看这个神奇的函数wm_concat(列名),该函 ...

  6. hdu 4544 湫湫系列故事——消灭兔子(优先队列)

    题意:n只兔子(有血量),m只箭(有伤害.花费),每只兔子只能被射一次,求射死所有兔子的最少花费. 思路:贪心,2重循环,兔子从血量高到低,箭从伤害高到低,用能射死兔子的箭中花费最小的箭射. #inc ...

  7. 05:LGTB 与偶数

    总时间限制:  10000ms 单个测试点时间限制:  1000ms 内存限制:  65536kB 描述 LGTB 有一个长度为 N 的序列.当序列中存在相邻的两个数的和为偶数的话,LGTB 就能把它 ...

  8. css3 appearance 改变元素外观

    h5 input标签的默认样式去除: 去掉date类型<input>框的叉叉: ::-webkit-clear-button { -webkit-appearance: none; } 去 ...

  9. 是时候开刷NOI了

    整天挨着毛爷爷,压力好大.. 看毛爷爷即将炖完NOI,我的确也该刷了 原则是从头到尾自己想(虽然看了一次题解),可以不A掉. NOI2009 day1: T1 题目略神,我还是不讲了...(就这题我W ...

  10. finalize方法的作用

            finalize是在对象回收前做一些清扫工作,以及可清理栈上的内存,比如调用本地方法可能在栈上创建对象.即使对象不可达,也并不是非死不可,还是可以抢救一下,当一个对象没有与GCroot ...