对于Spark的初学者,往往会有一个疑问:Spark(如SparkRDDSparkSQL)在处理数据的时候,会将数据都加载到内存再做处理吗?

很显然,答案是否定的!

对该问题产生疑问的根源还是对Spark计算模型理解不透彻。

对于Spark RDD,它是一个分布式的弹性数据集,不真正存储数据。如果你没有在代码中调用persist或者cache算子,Spark是不会真正将数据都放到内存里的。

此外,还要考虑persist/cache的缓存级别,以及对什么进行缓存(比如是对整张表生成的DataSet缓存还是列裁剪之后生成的DataSet缓存)(关于Spark RDD的特性解析参考《Spark RDD详解》

既然Spark RDD不存储数据,那么它内部是如何读取数据的呢?其实Spark内部也实现了一套存储系统:BlockManager。为了更深刻的理解Spark RDD数据的处理流程,先抛开BlockManager本身原理,从源码角度阐述RDD内部函数的迭代体系。

我们都知道RDD算子最终会被转化为shuffle map task和result task,这些task通过调用RDD的iterator方法获取对应partition数据,而这个iterator方法又会逐层调用父RDD的iterator方法获取数据(通过重写scala.collection.iterator的hasNext和next方法实现)。主要过程如下:

首先看ShuffleMapTask和ResultTask中runTask方法的源码:

关键看这部分处理逻辑:

rdd.iterator(partition, context)

getOrCompute方法会先通过当前executor上的BlockManager获取指定blockId的block,如果block不存在则调用computeOrReadCheckpoint,如果要处理的RDD没有被checkpoint或者materialized,则接着调用compute方法进行计算。

compute方法是RDD的抽象方法,由继承RDD的子类具体实现。

以WordCount为例:

sc.textFile(input)
.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
.saveAsTextFile(output)
  1. textFile会构建一个HadoopRDD

  2. flatMap/map会构建一个MapPartitionsRDD

  3. reduceByKey触发shuffle时会构建一个ShuffledRDD

  4. saveAsTextFile作为action算子会触发整个任务的执行

以flatMap/map产生的MapPartitionsRDD实现的compute方法为例:

override def compute(split: Partition, context: TaskContext): Iterator[U] =
f(context, split.index, firstParent[T].iterator(split, context))

底层调用了parent RDD的iterator方法,然后作为参数传入到了当前的MapPartitionsRDD。而f函数就是对parent RDD的iterator调用了相同的map类函数以执行用户给定的函数。

所以,这是一个逐层嵌套的rdd.iterator方法调用,子RDD调用父RDD的iterator方法并在其结果之上调用Iterator的map函数以执行用户给定的函数,逐层调用直到调用到最初的iterator(比如上述WordCount示例中HadoopRDD partition的iterator)。

而scala.collection.Iterator的map/flatMap方法返回的Iterator就是基于当前Iterator重写了next和hasNext方法的Iterator实例。比如,对于map函数,结果Iterator的hasNext就是直接调用了self iterator的hasNext,next方法就是在self iterator的next方法的结果上调用了指定的map函数。

flatMap和filter函数稍微复杂些,但本质上一样,都是通过调用self iterator的hasNext和next方法对数据进行遍历和处理。

所以,当我们调用最终结果iterator的hasNext和next方法进行遍历时,每遍历一个数据元素都会逐层调用父层iterator的hasNext和next方法。各层的map函数组成一个pipeline,每个数据元素都经过这个pipeline的处理得到最终结果。

这也是Spark的优势之一,map类算子整个形成类似流式处理的pipeline管道,一条数据被该链条上的各个RDD所包裹的函数处理。

再回到WordCount例子。HadoopRDD直接跟数据源关联,内存中存储多少数据跟读取文件的buffer和该RDD的分区数相关(比如buffer*partitionNum,当然这是一个理论值),saveAsTextFile与此类似。MapPartitionsRDD里实际在内存里的数据也跟partition数有关系。ShuffledRDD稍微复杂些,因为牵扯到shuffle,但是RDD本身的特性仍然满足(记录文件的存储位置)。

说完了Spark RDD,再来看另一个问题:Spark SQL对于多表之间join操作,会先把所有表中数据加载到内存再做处理吗?

当然,肯定也不需要!

具体可以查看Spark SQL针对相应的Join SQL的查询计划,以及在之前的文章《Spark SQL如何选择join策略》中,针对目前Spark SQL支持的join方式,任何一种都不要将join语句中涉及的表全部加载到内存。即使是Broadcast Hash Join也只需将满足条件的小表完整加载到内存。

推荐文章:

对Spark硬件配置的建议

Spark和MapReduce任务计算模型

Spark集群和任务执行

通过spark.default.parallelism谈Spark谈并行度

解析SparkStreaming和Kafka集成的两种方式

Spark为什么只有在调用action时才会触发任务执行呢(附算子优化和使用示例)?


关注微信公众号:大数据学习与分享,获取更对技术干货

Spark在处理数据的时候,会将数据都加载到内存再做处理吗?的更多相关文章

  1. [WP8.1UI控件编程]Windows Phone大数据量网络图片列表的异步加载和内存优化

    11.2.4 大数据量网络图片列表的异步加载和内存优化 虚拟化技术可以让Windows Phone上的大数据量列表不必担心会一次性加载所有的数据,保证了UI的流程性.对于虚拟化的技术,我们不仅仅只是依 ...

  2. Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(例如顺序:1、初始化spring容器,2、初始化线程池,3、加载业务代码,将数据库中数据加载到内存中)

    最近公司要做功能迁移,原来的后台使用的Netty,现在要迁移到在uap上,也就是说所有后台的代码不能通过netty写的加载顺序加载了. 问题就来了,怎样让迁移到tomcat的代码按照原来的加载顺序进行 ...

  3. js 鼠标滚动到某屏时,加载那一屏的数据,仿京东首页楼层异步加载模式

    js用处:在做商城时,首页图片太多,严重影响首页打开速度,所以我们需要用到异步加载楼层.js名称:鼠标滚动到某屏时,加载那一屏的数据,仿京东首页楼层模式js解释:1.用于商城的楼层内容异步加载,滚动条 ...

  4. Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(优先初始化Spring IOC容器)

    JavaWebSpringTomcatCache  最近用到在Tomcat服务器启动时自动加载数据到缓存,这就需要创建一个自定义的缓存监听器并实现ServletContextListener接口,并且 ...

  5. Highcharts 基本曲线图;Highcharts 带有数据标签曲线图表;Highcharts 异步加载数据曲线图表

    Highcharts 基本曲线图 实例 文件名:highcharts_line_basic.htm <html> <head> <meta charset="U ...

  6. python数据可视化-matplotlib入门(7)-从网络加载数据及数据可视化的小总结

    除了从文件加载数据,另一个数据源是互联网,互联网每天产生各种不同的数据,可以用各种各样的方式从互联网加载数据. 一.了解 Web API Web 应用编程接口(API)自动请求网站的特定信息,再对这些 ...

  7. html ajax请求 php 下拉 加载更多数据 (也可点击按钮加载更多)

    <input type="hidden" class="total_num" id="total" value="{$tot ...

  8. EF如何操作内存中的数据以及加载相关联表的数据:延迟加载、贪婪加载、显示加载

    之前的EF Code First系列讲了那么多如何配置实体和数据库表的关系,显然配置只是辅助,使用EF操作数据库才是每天开发中都需要用的,这个系列讲讲如何使用EF操作数据库.老版本的EF主要是通过Ob ...

  9. EF如何操作内存中的数据和加载外键数据:延迟加载、贪婪加载、显示加载

    EF如何操作内存中的数据和加载外键数据:延迟加载.贪婪加载.显示加载 之前的EF Code First系列讲了那么多如何配置实体和数据库表的关系,显然配置只是辅助,使用EF操作数据库才是每天开发中都需 ...

随机推荐

  1. 开发环境管理利器Vagrant

    引言 不知道你是否经历过,开发环境与生产环境不一致.Windows开发和Linux上的包效果不一样.在我这运行时好的啊 等等等问题,那有没有解决方法呢? 答案就是Vagrant.Docker 1.简介 ...

  2. C语言之数据在内存中的存储

    C语言之数据在内存中的存储 在我们学习此之前,我们先来回忆一下C语言中都有哪些数据类型呢? 首先我们来看看C语言中的基本的内置类型: char //字符数据类型 short //短整型 int //整 ...

  3. cassandra权威指南读书笔记--监控&维护

    cassandra SLF4J API记录日志,具体采用Logback实现.在终端启动cassandra时,可以向程序输入-f,就能在终端看到这个输出.Java Mangement Extension ...

  4. Kafka调试入门(一)

    很多人对kafka消息队列应该不陌生,使用起来也比较方便.对kafka最常见的操作一般有如下几种: 启动kafka集群 创建一个名称为xxx的主题(topic) 查看已经创建好的主题 向xxx这个主题 ...

  5. ZOJ3640-Help Me Escape 概率dp

    题意: 在一个迷宫中有n条路经,你会被随机传送到一条路径,每条路径有一个挑战难度ci,你最初有一个战斗力f,如果你的战斗力大于ci,那么呆在那里ti天就可以成功逃出迷宫.如果你的战斗力小于等于ci,那 ...

  6. Acwing 154 滑动窗口(单调队列)经典模板

    给定一个大小为n≤106n≤106的数组. 有一个大小为k的滑动窗口,它从数组的最左边移动到最右边. 您只能在窗口中看到k个数字. 每次滑动窗口向右移动一个位置. 以下是一个例子: 该数组为[1 3 ...

  7. Codeforces Round #481 (Div. 3) D. Almost Arithmetic Progression (暴力)

    题意:有一个长度为\(n\)的序列,可以对所有元素++或--,求最少的操作次数,如果无论如何都不能构成,则输出\(-1\). 题解:一个等差数列一定由首项\(a_{1}\)和公差\(d\)来决定,而这 ...

  8. Educational Codeforces Round 91 (Rated for Div. 2) A. Three Indices (模拟)

    题意:有一长度为\(n\)的序列,问是否能找到\(a_{i}<a_{j},a_{j}>a_{k},(i<j<k)\),如果满足,输出其位置. 题解:直接暴力两头找即可,最坏复杂 ...

  9. EFCore学习记录--数据访问技术人门2

    1 code fist 1.创建实体类: 2.创建DbContext类: mysql连接字符串是:Server=127.0.0.1;Port=3306;Database=BlogDb; User=ro ...

  10. PowerShell随笔9--- call

    很多时候我们需要在一个脚本文件执行另外一个脚本文件,比如我们有一个Test.ps1文件 我们有以下2种方法: Invoke-Expression (&) 我们可以看到,Test.ps1中的代码 ...