二、RDD编程

1. 什么是RDD?

弹性分布式数据集(Resilient Distributed Dataset,简称 RDD)。RDD 其实就是分布式的元素集合。在Spark 中,对数据的所有操作不外乎创建 RDD、转化已有 RDD 以及调用 RDD 操作进行求值。而在这一切背后,Spark会自动将RDD 中的数据分发到集群上,并将操作并行化执行。

RDD是一种对数据集形态的抽象,基于此抽象,使用者可以在集群中执行一系列计算,而不用将中间结果落盘。(这正是之前MR抽象的一个重要痛点,每一个步骤都需要落盘,使得不必要的开销很高)
对于分布式系统,容错支持是必不可少的,RDD只支持粗粒度的变换即,输入数据集是 immutable (或者说只读)的,每次运算会产生新的输出。不支持对一个数据集中细粒度的更新操作。这种约束,大大简化了容错支持,并且能满足很大一类的计算需求。

(1):RDD抽象

原文地址:https://zhuanlan.zhihu.com/p/91749572
作者:扎心了,老铁

RDD是一个基于分区的,只读的数据记录集抽象。RDD只可以通过对持久存储或其他RDD进行确定性运算得来,这种运算被称为变换。常用的变换算子包括:map,filter,join等。
RDD没有选择不断地做检查点来进行容错,而是会几下RDD从最初的外存的数据集变化而来的变化路径,也就是谱系(lineage)。理论上所有的RDD都可以在出错后从外存中依据谱系图进行重建。一般来说,重建的粒度是分区(Partition)而非整个数据集,一来代价更小,二来不同分区可能在不同的机器上。
用户可以对 RDD 的两个方面进行控制:持久化和分区控制。对于前者,如果某些 RDD 需要复用,那么用户可以指示系统按照某种策略将其进行持久化。后者来说,用户可以定制分区路由函数,将数据集合中的记录按照某个键值路由到不同分区。比如进行 Join 操作的时候,可以讲待 Join 数据集按照相同的策略进行分区,以并行 Join。
转化操作(transformation):会由一个 RDD 生成一个新的 RDD(用户使用 RDD 时,首先将数据从持久化存储中通过变换(Transformations,如 map 或者 filter)将其载入内存)
行动操作(action):会对 RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结果存储到外部存储系统(如 HDFS)中
惰性计算:你可以在任意时刻定义RDD,但是spark只有在对于该RDD的第一个行动操作时才会真正计算。

Spark 的 RDD 会在你每次对它们进行行动操作时重新计算。如果想在多个行动操作中重用同一个 RDD,可以使用RDD.persist() 让 Spark 把这个 RDD 缓存下来。
RDD的五个属性

  1. 一组分片(Partition),即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。
  2. 一个Partitioner,即RDD的分片函数。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
  3. 一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。
  4. 一个计算每个分区的函数。Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。
  5. 一个列表,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。

2.WordCount图解

在这里插入图片描述
其中hello.txt
在这里插入图片描述

2.spark会把数据都载入到内存吗?

作者:祝威廉
链接:https://www.jianshu.com/p/b70fe63a77a8
来源:简书

(1)RDD的本质是什么?

一个RDD本质上是一个函数,而RDD的变换不过是函数的嵌套。RDD有两类:

  1. 输入RDD,典型如KafkaRDD,JdbcRDD
  2. 转换RDD,如MapPartitionsRDD
    例如下面的一段代码:
sc.textFile("abc.log").map().saveAsTextFile("")

textFile会构建出一个NewHadoopRDD,
map函数运行后会构建出一个MapPartitionsRDD
saveAsTextFile触发了实际流程代码的执行
NewHadoopRDD是数据来源,每个parition负责获取数据,获得过程是通过iterator.next 获得一条一条记录的。假设某个时刻拿到了一条数据A,这个A会立刻被map里的函数处理得到B(完成了转换),然后开始写入到HDFS上。其他数据重复如此。所以整个过程:
理论上某个MapPartitionsRDD里实际在内存里的数据等于其Partition的数目,是个非常小的数值。
NewHadoopRDD则会略多些,因为属于数据源,读取文件,假设读取文件的buffer是1M,那么最多也就是partitionNum1M 数据在内存里
saveAsTextFile也是一样的,往HDFS写文件,需要buffer,最多数据量为 buffer
partitionNum
所以整个过程是流式的过程,一条数据被各个RDD所包裹的函数处理。


按照上面的逻辑,内存使用其实是非常小的,10G内存跑100T数据也不是难事。但是为什么spark常常因为内存问题挂掉呢?

(2)Shuffle的本质是什么?

这就是为什么要分Stage了。每个Stage其实就是我上面说的那样,一套数据被N个嵌套的函数处理(也就是你的transform动作)。遇到了Shuffle,就被切开来,所谓的Shuffle,本质上是把数据按规则临时都落到磁盘上,相当于完成了一个saveAsTextFile的动作,不过是存本地磁盘。然后被切开的下一个Stage则以本地磁盘的这些数据作为数据源,重新走上面描述的流程。

我们再做一次描述:

所谓Shuffle不过是把处理流程切分,给切分的上一段(我们称为Stage M)加个存储到磁盘的Action动作,把切分的下一段(Stage M+1)数据源变成Stage M存储的磁盘文件。每个Stage都可以走我上面的描述,让每条数据都可以被N个嵌套的函数处理,最后通过用户指定的动作进行存储。

(3)为什么Shuffle 容易导致Spark挂掉

前面我们提到,Shuffle不过是偷偷的帮你加上了个类似saveAsLocalDiskFile的动作。然而,写磁盘是一个高昂的动作。所以我们尽可能的把数据先放到内存,再批量写到文件里,还有读磁盘文件也是给费内存的动作。把数据放内存,就遇到个问题,比如10000条数据,到底会占用多少内存?这个其实很难预估的。所以一不小心,就容易导致内存溢出了。这其实也是一个很无奈的事情。

(4)Cache/Persist意味着什么?

其实就是给某个Stage加上了一个saveAsMemoryBlockFile的动作,然后下次再要数据的时候,就不用算了。这些存在内存的数据就表示了某个RDD处理后的结果。这个才是说为啥Spark是内存计算引擎的地方。在MR里,你是要放到HDFS里的,但Spark允许你把中间结果放内存里。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值