Spark-RDD编程之RDD特性

Apache将RDD定义为弹性分布式数据集,它是Spark应用程序中数据的基本组织形式。弹性意味着RDD能够自动地进行内存和磁盘数据存储的切换,并且具有非常高的容错性;分布式说明RDD是一个存储在多个节点上的海量数据集合。RDD是一种高度受限的共享内存模型,即RDD是只读的记录分区的集合。RDD具有自动容错、位置感知调度和可伸缩性等数据流模型的特点。

一:分区

RDD中的数据可能是TB、PB级别的,完全基于一个节点上的存储与计算并不现实。Spark基于分布式的思想,先将RDD划分为若干子集,每个子集称为一个分区(partition)。分区是RDD的基本组成单位,与MapReduce中的split类似。对于一个RDD,Spark以分区为单位逐个计算分区中的元素。分区的多少决定了这个RDD进行并行计算的粒度,因为对RDD中每一个分区的计算都是在一个单独的任务中执行的。用户可以显示指定RDD的分区数目,若不指定,将采用默认值(即CPU核数)

#指定RDD分区数目为4

scala>val rdd=sc.parallelize(1 to 5,4)

scala>rdd.partitions.size

res0:Int=4

#查看RDD默认分区数目

scala>val rdd=sc.parallelize(1 to 5)

scala>rdd.partitions.size

res0:Int=2

二:依赖

RDD的依赖:

窄依赖(也叫narrow依赖)
从父RDD角度看:一个父RDD只被一个子RDD分区使用。父RDD的每个分区最多只能被一个Child RDD的一个分区使用
从子RDD角度看:依赖上级RDD的部分分区     精确知道依赖的上级RDD分区,会选择和自己在同一节点的上级RDD分区,没有网络IO开销,高效。如map,flatmap,filter

宽依赖(也叫shuffle依赖/wide依赖)
从父RDD角度看:一个父RDD被多个子RDD分区使用。父RDD的每个分区可以被多个Child RDD分区依赖
从子RDD角度看:依赖上级RDD的所有分区     无法精确定位依赖的上级RDD分区,相当于依赖所有分区(例如reduceByKey)  计算就涉及到节点间网络传输  



为何要区分这两种依赖关系?一方面,对于若干个彼此依赖关系的RDD,基于任何一个子RDD分区可以方便地计算出其所有祖先的RDD相应分区,这些RDD可以在集群的节点的内存中以流水线(pipeline)的方式高效执行


另一方面,对于窄依赖关系间的RDD,当子RDD的一个分区出错,可以很方便地利用父RDD中对应的分区重新计算该错误分区,因此窄依赖关系使得数据的容错与恢复非常方便;而对于宽依赖关系,子RDD的一个分区出错会导致其对应父RDD的多个分区重新计算,过程类似于MapReduce的Shuffle操作,代价非常高。

scala>val rdd=sc.makeRDD(1 to 10)

scala>val mapRDD=rdd.map(x=>(x,x))

scala>mapRDD.dependencies

#返回窄依赖关系

res2: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.OneToOneDependency@1cec26b4)


scala>val shuffleRDD=mapRDD.partitionBy(new org.apache.spark.HashPartitioner(3))

scala>shuffleRDD.dependencies

#返回宽依赖关系

res3: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.ShuffleDependency@51f2df61)

三:优先位置

RDD优先位置属性与Spark中的作业调度与管理相关,是RDD中每个分区所存储的位置。遵循“移动计算不移动数据”这一理念,Spark在执行任务时尽可能地将计算分配到相关数据块所在的节点上。

四:创建操作

RDD中封装的操作非常丰富,可以大致分为转换操作(Transformation)和执行操作(Action)两类,在已知RDD上执行转换操作可返回一个新的RDD;而执行操作则是向驱动器程序返回结果或把结果写入外部系统。转换操作采用了惰性策略,即转换操作并不会立即被计算,其相当于业务逻辑的一种抽象描述,只有提交执行操作,才会真正触发对转换操作的计算。得到一个RDD通常有三种方式:对驱动程序中集合的并行化处理、读取外部存储系统(HDFS、Hbase、Hive等)、基于已有RDD的转换。

  Spark提供了parallelize和makeRDD两个操作来实现从程序中的已有集合创建RDD。这两个操作功能类似,不同是makeRDD还提供了一个可以指定每一个分区preferredLocations参数的实现版本,makeRDD不仅将集合按顺序平均分片,还可以指定每一个RDD分区的优先位置(preferredLocations),以便在后续的运行中优化调度。

#利用makeRDD创建RDD

scala>val rdd=sc.makeRDD(1 to 10, 3)

scala>rdd.collect

res4: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)


#利用parallelize创建RDD

scala>val rdd=sc.parallelize(1 to 10)

scala>rdd.collect

然而这种方式需要将整个数据集先存放在一台物理节点的内存中,因此在实际应用场景中并不多见,而通常是采用读取外部数据集的方式来创建RDD的。

Spark支持的Hadoop输入格式包括文本文件、SequenceFile、Avro、Parquet等。相应的Spark提供了textFile、HadoopFile、sequenceFile等一系列操作来读取相应类型的文件创建RDD。

#textFile读取HDFS上指定的文件创建RDD

scala>val rdd=sc.textFile("hdfs://master:9000/user/mark/input/file.txt")

scala>rdd.collect

读取HDFS上、user/mark/input目录下中的file.txt文件创建RDD,file.txt文件内容如下:

hello spark

hello hbase

spark spark

运行结果

res0:Array[String]=Array(hello spark,hello hbase,spark spark)

textFile读取HDFS上指定目录中所有文件创建RDD

scala>val rdd=sc.textFile("hdfs://master:9000/user/mark/input")

textFile读取HDFS上指定目录中所有扩展名为.gz的文件创建RDD(textFile只支持.gz格式的压缩文件)

scala>val rdd =sc.textFile("hdfs://master:9000/user/mark/input/*.gz")

借助通配符创建RDD

scala>val rdd =sc.textFile("hdfs://master:9000/user/mark/input/*.txt")

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值