Spark开发-RDD接口编程

来源:互联网 发布:电子文档软件 编辑:程序博客网 时间:2024/05/17 05:01

一般情况下面RDD包括5个接口

partition 分区,一个RDD会有一个或者多个分区
preferredLocations(P) 对于分区P而言,返回数据本地化计算的节点
dependencies() RDD的依赖关系
compute(p,context) 对于分区P而言,进行迭代计算
partitioner() RDD的分区函数

RDD分区 partitions

既然RDD是一个分区的数据集,那么RDD肯定具备分区的属性,对于一个RDD而言,分区的多少涉及对这个RDD进行并行计算的粒度,每一个RDD分区的计算操作都在一个单独的任务中被执行,对于RDD的分区而言,用户可以自行指定多少分区,如果没有指定,那么将会使用默认值,可以利用RDD的成员变量partitions所返回的partition数组的大小来查询一个RDD被划分的分区数,我们通过parallelize来指定并行度

scala> val rdd=sc.parallelize(1 to 100,2)rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:27scala> rdd.partitions.sizeres0: Int = 2

当然也可以在创建RDD的时间不指定分区,系统默认的数值是这个程序所分配到的资源的CPU核的个数

scala> val rdd=sc.parallelize(1 to 100)rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[1] at parallelize at <console>:27scala> rdd.partitions.sizeres1: Int = 24

RDD优先位置(preferredLocations)

RDD优先位置属性与spark中的调度相关,返回的是此RDD的每个partition所存储的位置,按照“移动数据不如移动计算”的理念,在spark进行任务调度的时候,尽可能地将任务分配到数据块所存储的位置,以从Hadoop中读取数据生成RDD为例,preferredLocation返回每一个数据块所在的机器名或者IP地址,如果每一块数据是多份存储的,那么就会返回多个机器地址.

在下面的程序中,首先通过sparkContext的textFile函数读取一个13MB的文件,生成一个类型为MappedRDD的rdd,然后通过rdd的依赖关系找到原始的HadoopRDD,HadoopRDD的partition的个数为2个,对于第一个partition而言,其preferredLocations返回了三个机器地址,以便后续调度的程序根据这个地址更加有效地分配任务.

scala> val rdd=sc.textFile("/data/wikiticker-2015-09-12-sampled.json")17/10/03 22:57:11 INFO storage.MemoryStore: Block broadcast_0 stored as values in memory (estimated size 229.9 KB, free 2.7 GB)17/10/03 22:57:12 INFO storage.MemoryStore: Block broadcast_0_piece0 stored as bytes in memory (estimated size 19.5 KB, free 2.7 GB)17/10/03 22:57:12 INFO storage.BlockManagerInfo: Added broadcast_0_piece0 in memory on 192.168.18.140:52808 (size: 19.5 KB, free: 2.7 GB)17/10/03 22:57:12 INFO spark.SparkContext: Created broadcast 0 from textFile at <console>:27rdd: org.apache.spark.rdd.RDD[String] = /data/wikiticker-2015-09-12-sampled.json MapPartitionsRDD[3] at textFile at <console>:27scala> val hadoopRDD=rdd.dependencies(0).rddhadoopRDD: org.apache.spark.rdd.RDD[_] = /data/wikiticker-2015-09-12-sampled.json HadoopRDD[2] at textFile at <console>:27scala> hadoopRDD.partitions.size17/10/03 22:59:19 INFO mapred.FileInputFormat: Total input paths to process : 1res2: Int = 2scala> hadoopRDD.preferredLocations(hadoopRDD.partitions(0))res4: Seq[String] = ListBuffer(slave3, slave1, slave2)

RDD依赖关系(dependencies)

dependencies顾名思义就是依赖的意思,由于RDD是粗粒度的操作数据集,每一个转换操作都会生成一个新的RDD,所以RDD之间就会形成类似于流水一样的前后依赖关系,在spark中存在两种类型的依赖,即窄依赖(Narrow dependencies)和宽依赖(wide dependencies)
1、窄依赖:每一个父RDD的分区最多只被子RDD的一个分区所使用
2、宽依赖:多个子RDD的分区会依赖于同一个父RDD的分区,

下面这个图很好的说明了窄依赖和宽依赖
这里写图片描述

一个矩形表示一个RDD,在矩形中的椭圆形表示这个RDD的一个分区,例如 操作Map和Filter就会形成一个窄依赖,而没有经过co-paritioned操作的两个RDD数据集之间进行join操作就会形成宽依赖,在spark中需要明确地区分这两种依赖关系有2个方面的原意,一:窄依赖可以在集群的一个节点上如流水线一般的执行,可以计算所有父RDD的分区,相反的,宽依赖需要取得父RDD的所有分区上的数据进行计算,将会执行类似于MapReduce一样的shuffle操作,二:对于窄依赖来说,节点计算失败后的恢复会更加有效,只需要重新计算对应的父RDD的分区,而且可以在其他的节点上并行地计算,相反的,在有宽依赖的继承关系中,一个节点的失败将会导致其父RDD的多个分区重新计算,这个代价是非常高的,org.apache.spark.OneToOneDepengency窄依赖和org.apache.spark.ShuffleDependency宽依赖
scala> val rdd=sc.makeRDD(1 to 10)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[4] at makeRDD at :27

scala> val MapRDD=rdd.map(x=>(x,x))
MapRDD: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[5] at map at :29

scala> MapRDD.dependencies
res5: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.OneToOneDependency@3c51d0a7)

scala> val shuffleRDD=MapRDD.partitionBy(new org.apache.spark.HashPartitioner(3))
shuffleRDD: org.apache.spark.rdd.RDD[(Int, Int)] = ShuffledRDD[6] at partitionBy at :31

scala> shuffleRDD.dependencies
res6: Seq[org.apache.spark.Dependency[_]] = List(org.apache.spark.ShuffleDependency@5cdcbeb5)