Spark原理篇之RDD特征分析讲解

      RDD(Resilient Distributed DataSets)弹性分布式数据集,是分布式内存的一个抽象概念。我们可以抽象地代表对应一个HDFS上的文件,但是它实际上是被分区的,分为多个分区洒落在Spark集群中的不同节点上。

1 RDD五大特性

在这里插入图片描述
(1)A list of partitions:RDD是由一组partition组成。例如要读取hdfs上的文本文件的话,可以使用textFile()方法把hdfs的文件加载过来,把每台机器的数据放到partition中,并且封装了一个HadoopRDD,这就是一个抽象的概念。每一个partition都对应了机器中的数据。因为在hdfs中的一个Datanode,有很多的block,读机器的数据时,会将每一个block变成一个partition,与MapReduce中split的大小由min split,max split,block size (max(min split, min(max split, block size)))决定的相同,spark中的partition大小实际上对应了一个split的大小。经过转化,HadoopRDD会转成其他RDD,如FilteredRDD、PairRDD等,但是partition还是相应的partition,只是因为有函数应用里面的数据变化了。
(2)A function for computing each split:在HadoopRDD中,每个partition相当于一个split。一个函数应用在一个RDD上,可以理解为一个函数对集合(RDD)内的每个元素(partition)的操作。从上图可以看出HadoopRDD中的partition需要函数的作用才能转换到FilterRDD中的partition。
(3)A list of dependencies on other RDDs:RDD是有依赖的。PairRDD的依赖是HadoopRDD和FilterRDD,FilterRDD的依赖是HadoopRDD。
(4)Optionally, a Partitioner for key-value RDDs:如果数据是key-value键值对形式们可以传递一个partitioner来对RDD进行自定义分区。
(5)Optionally,a list of preferred locations to compute each split on: textFile()过程中,可以指定加载到性能好的机器中。例如,HDFS中的数据可能放在一大堆破旧的机器上,HDFS数据在磁盘上,磁盘可能很大,CPU、内存的性能很差。Spark默认做的事情是,把数据加载进来,会把数据抽象成一个RDD,抽象进来的数据在内存中,这内存指的是本机的内存,这是因为在分布式文件系统中,要遵循数据本地性原则,即移动计算(把函数、jar包发过去)而不移动数据(移动数据成本较高)。而一般HDFS的集群机器的内存比较差,如果要把这么多数据加载到烂机器的内存中,会存在问题,一是内存可能装不下,二是CPU差、计算能力差,这就等于没有发挥出spark的性能。在这种情况下,Spark的RDD可以提供一个可选项,可以指定一个preferred locations,即指定一个位置来加载数据。这样就可以指定加载到性能好的机器去计算。例如,可以将hdfs数据加载到Tachyon内存文件系统中,然后再基于Tachyon来做Spark程序。

2 RDD两种操作

2.1 Transformation

      Transformation用于对RDD的创建,RDD只能使用Transformation创建,同时还提供大量操作方法,包括map、filter、groupBy和join等,RDD利用这些操作生成新的RDD,但是需要注意,无论多少次Transformation,在RDD中真正数据计算Action之前都不可能真正运行。

2.2 Action

      Action是数据执行部分,其通过执行count、reduce和collect等方法真正执行数据的计算部分。实际上,RDD中所有的操作都是lazy模式进行,运行在编译中不会立即计算最终结果,而是胡这个月所有操作步骤和方法,只有遇到启动命令才执行。这样做的好处在于大部分前期工作在Transformation时已经完成,当Action工作时,只需要完成业务的核心工作。

2.3 函数概览
2.3.1 Transformation操作函数

(1)map(func):对调用map的RDD数据集中的每个element都使用func,然后返回一个新的RDD,这个返回的数据集是分布式的数据集。
(2) filter(func) : 对调用filter的RDD数据集中的每个元素都使用func,然后返回一个包含使func为true的元素构成的RDD。
(3)flatMap(func):和map差不多,但是flatMap生成的是多个结果。
(4)mapPartitions(func):和map很像,但是map是每个element,而mapPartitions是每个partition。
(5)mapPartitionsWithSplit(func):和mapPartitions很像,但是func作用的是其中一个split上,所以func中应该有index。
(6)sample(withReplacement,faction,seed):抽样。
(7)union(otherDataset):返回一个新的dataset,包含源dataset和给定dataset的元素的集合。
(8)distinct([numTasks]):返回一个新的dataset,这个dataset含有的是源dataset中的distinct的element。
(9)groupByKey(numTasks):返回(K,Seq[V]),也就是hadoop中reduce函数接受的key-valuelist。
(10)reduceByKey(func,[numTasks]):就是用一个给定的reduce func再作用在groupByKey产生的(K,Seq[V]),比如求和,求平均数。
(11)sortByKey([ascending],[numTasks]):按照key来进行排序,是升序还是降序,ascending是boolean类型。
(12)join(otherDataset,[numTasks]):当有两个KV的dataset(K,V)和(K,W),返回的是(K,(V,W))的dataset,numTasks为并发的任务数。
(13)cogroup(otherDataset,[numTasks]):当有两个KV的dataset(K,V)和(K,W),返回的是(K,Seq[V],Seq[W])的dataset,numTasks为并发的任务数。
(14)cartesian(otherDataset):笛卡尔积就是m*n,大家懂的。

2.3.2 Action操作函数

(1)reduce(func):说白了就是聚集,但是传入的函数是两个参数输入返回一个值,这个函数必须是满足交换律和结合律的。
(2)collect():一般在filter或者足够小的结果的时候,再用collect封装返回一个数组。
(3)count():返回的是dataset中的element的个数。
(4)first():返回的是dataset中的第一个元素。
(5)take(n):返回前n个elements,这个士driver program返回的。
(6)takeSample(withReplacement,num,seed):抽样返回一个dataset中的num个元素,随机种子seed。
(7)saveAsTextFile(path):把dataset写到一个text file中,或者hdfs,或者hdfs支持的文件系统中,spark把每条记录都转换为一行记录,然后写到file中。
(8)saveAsSequenceFile(path):只能用在key-value对上,然后生成SequenceFile写到本地或者hadoop文件系统。
(9)countByKey():返回的是key对应的个数的一个map,作用于一个RDD。
(10)foreach(func):对dataset中的每个元素都使用func。

3 RDD缓存策略

(1)NONE

  val NONE = new StorageLevel(false, false, false, false)

      表示不需要缓存。(不使用磁盘,不用内存,使用堆,序列化)
(2)DISK_ONLY

  val DISK_ONLY = new StorageLevel(true, false, false, false)

      表示使用磁盘。(使用磁盘,不用内存,使用堆,序列化)
(3)DISK_ONLY_2

  val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)

      DISK_ONLY_2表示使用磁盘,两个副本。(使用磁盘,不用内存,使用堆,序列化,2)
(4)MEMORY_ONLY

  val MEMORY_ONLY = new StorageLevel(false, true, false, true)

      MEMORY_ONLY表示只使用内存,例如1G的数据要放入512M的内存,会将数据切成两份,先将512M加载到内存,剩下的512M还在原来位置(如HDFS),之后如果有RDD的运算,会从内存和磁盘中去找各自的512M数据。(不使用磁盘,使用内存,使用堆,不序列化)
(5)MEMORY_ONLY_2

  val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)

      MEMORY_ONLY_2表示只使用内存,2个副本。(不使用磁盘,使用内存,使用堆,不序列化,2)
(6)MEMORY_ONLY_SER

  val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)

      MEMORY_ONLY_SER表示只使用内存,序列化。(不使用磁盘,使用内存,使用堆,序列化)
(7)MEMORY_ONLY_SER_2

  val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)

      MEMORY_ONLY_SER表示只使用内存,序列化,2个副本。(不使用磁盘,使用内存,使用堆,序列化,2)
(8)MEMORY_AND_DISK

  val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)

      MEMORY_AND_DISK和MEMORY_ONLY很类似,都使用到了内存和磁盘,只是使用的是本机本地磁盘,例如1G数据要加载到512M的内存中,首先将HDFS的1G数据的512M加载到内存,另外的512M加载到本地的磁盘缓存着(和HDFS就没有关系了),RDD要读取数据的话就在内存和本地磁盘中找。(使用磁盘,使用内存,使用堆,不序列化)
(9)MEMORY_AND_DISK_2

  val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)

      MEMORY_AND_DISK_2表示两个副本。(使用磁盘,使用内存,使用堆,不序列化,2)
(10)MEMORY_AND_DISK_SER

  val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)

      MEMORY_AND_DISK_SER本地内存和磁盘,序列化。序列化的好处在于可以压缩,但是压缩就意味着要解压缩,需要消耗一些CPU。
(11)MEMORY_AND_DISK_SER_2

  val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)

      MEMORY_AND_DISK_SER2,两个副本。
(12)OFF_HEAP

  val OFF_HEAP = new StorageLevel(false, false, true, false)

      OFF_HEAP不使用堆内存(例如可以使用Tachyon的分布式内存文件系统)。(不使用磁盘,不用内存,不使用堆,序列化)
4 RDD Lineage(血统)
在这里插入图片描述
      一系列RDD到RDD的transformation操作,称为lineage(血统)。某个RDD依赖于它前面的所有RDD。例如一个由10个RDD到RDD的转化构成的lineage,如果在计算到第9个RDD时失败了,一般较好的计算框架会自动重新计算。一般地,这种错误发生了会去找上一个RDD,但是实际上如果不做缓存是找不到的,因为即使RDD9知道它是由RDD8转化过来的,但是因为它并没有存RDD数据本身,在内存中RDD瞬时转化,瞬间就会在内存中消失,所以还是找不到数据。如果这时RDD8做过cache缓存,那么就是在RDD8的时候进行了数据的保存并记录了位置,这时如果RDD9失败了就会从缓存中读取RDD8的数据;如果RDD8没有做cache就会找RDD7,以此类推,如果都没有做cache就需要重新从HDFS中读取数据。所以所谓的容错就是指,当计算过程复杂,为了降低因某些关键点计算出错而需要重新计算的带来的惨重代价的风险,则需要在某些关键点使用cache或用persist方法做一下缓存。

5 容错

      上述缓存策略还存在一个问题。使用cache或persist的缓存策略是使用默认的仅在内存,所以实际的RDD缓存位置是在内存当中,如果机器出现问题,也会造成内存中的缓存RDD数据丢失。所以可以将要做容错的RDD数据存到指定磁盘(可以是KDFS)路径中,可以对RDD做doCheckpoint ()方法。使用doCheckpoint()方法的前提是需要在sc中预先设置SparkContext.setCheckpointDir(),设置数据存储路径。这时候如果程序计算过程出错了,会先到cache中找缓存数据,如果cache中没有就会到设置的磁盘路径中找。

6 RDD特征总结

6.1 关键特征

(1)分布式特征
在这里插入图片描述
      RDD(Resilient Distributed Datasets)弹性分布式数据集,是分布式内存的一个抽象概念。我们可以抽象的代表对应一个HDFS上的文件,但是他实际上是被分区的,分为多个分区撒落在Spark集群中的不同节点上。比如现在我们的一个RDD有40万条数据,并分为4个partition,这4个分区数据分别存储在集群中的节点1、2、3、4中,而每个partition分到10万条数据。如图1所示,这样的一个RDD将数据分布式撒落在集群的一批节点上,每个节点只是存储RDD的部分partition,这就是RDD的分布式结构模型。
(2)弹性式特征
在这里插入图片描述
      当RDD的每个partition数据都存放到Spark集群节点上时候,默认是都存放在内存中的,但是如果内存放不下这么多的数据时,我们该怎么办呢?这时候RDD的弹性特征就表现出来了。如上图2所示,在节点3内存中最多只能存储6万数据,结果我们需要存放一个partition数据为10万,那么这时就得把partition中的剩余4万数据写入到磁盘上进行保存了。而这种存储的分配针对用户是透明的。RDD的这种自动进行内存和磁盘之间权衡和却换的机制,就是RDD的弹性特征所在。
(3)容错性特征
      上面的内容已经讲到,这里不再进行赘述。

6.2 RDD特征概要总结

(1)RDD是Spark提供的核心抽象,全称为Resilient Distributed Datasets,即弹性分布式数据集。
(2)RDD在抽象上来说是一种元素集合,包含了数据。它是被分区的,分为多个分区,每个分区分布在集群中的不同节点上,从而让RDD中的数据可以被并行操作。
(3)RDD通常通过Hadoop上的文件,即HDFS文件或者Hive表,来进行创建;有时也可以通过应用程序中的集合来创建。
(4)RDD最重要的特性就是,提供了容错性,可以自动从节点失败中恢复过来。即如果某个节点上的RDD partition,因为节点故障,导致数据丢了,那么RDD会自动通过自己的数据来源重新计算该partition。这一切对使用者是透明的。
(5)RDD的数据默认情况下存放在内存中的,但是在内存资源不足时,Spark会自动将RDD数据写入磁盘。

参考文章:
[1] https://www.cnblogs.com/adienhsuan/p/5654485.html
[2] https://blog.csdn.net/egraldloi/article/details/16343733
[3] https://blog.csdn.net/helloxiaozhe/article/details/78481784
[4] https://blog.csdn.net/huidoo_yang/article/details/79778693
[5] http://www.raincent.com/content-85-11254-1.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值