Spark学习 RDD 概述


spark 为了达到高并发,高吞吐数据处理能力封装了三大数据对象;
  • RDD 弹性分布式数据集
  • 累加器:分布式共享只写变量
  • 广播变量:分布式共享只读变量

1、RDD 概述

RDD(Resilient Distributed DateSet) 弹性分布式数据集,是 Spark 最基本的数据处理类型,在代码中以抽象类方式出现。它代表了 一个弹性,不可变,可分区,内部元素可并行执行的计算集合;

1.1、RDD 特点

存储弹性,内存&磁盘自动切换; 容错弹性:数据丢失容错弹性 计算弹性:计算出错重试机制 分片弹性:可根据需要重新分片 分布式 数据存储在大数据平台的不同节点上; 数据集:RDD 封装了计算逻辑,不保存数据; 数据抽象:RDD 是一个抽象类,需要子类具体实现; 不可变:RDD 的计算逻辑是不可变的,要想修改计算逻辑,需要创建新的RDD; 可分区:并行计算;

1.2、RDD 核心属性

 * Internally, each RDD is characterized by five main properties:
 *
 *  - A list of partitions
 *  - A function for computing each split
 *  - A list of dependencies on other RDDs
 *  - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
 *  - Optionally, a list of preferred locations to compute each split on (e.g. block locations for
 *    an HDFS file)

1、分区列表 RDD 数据结构存放在分区列表中,用于实现并行计算,是分布式计算的基础属性;

 /**
   * Implemented by subclasses to return the set of partitions in this RDD. This method will only
   * be called once, so it is safe to implement a time-consuming computation in it.
   *
   * The partitions in this array must satisfy the following property:
   *   `rdd.partitions.zipWithIndex.forall { case (partition, index) => partition.index == index }`
   */
  protected def getPartitions: Array[Partition]

2、分区计算函数,spark 在计算时,使用分区计算函数对每一个分区进行计算

/**
 * :: DeveloperApi ::
 * Implemented by subclasses to compute a given partition.
 */
@DeveloperApi
def compute(split: Partition, context: TaskContext): Iterator[T]
3、RDD 之间依赖关系 当计算需要多个计算模型时组合时,需要依赖关系将多个RDD建立依赖关系; 
/**
 * Implemented by subclasses to return how this RDD depends on parent RDDs. This method will only
 * be called once, so it is safe to implement a time-consuming computation in it.
 */
protected def getDependencies: Seq[Dependency[_]] = deps
4、分区器 可选,当数据为 KV 数据时,可以设定分区自定义分区 
/**
 * Optionally overridden by subclasses to specify placement preferences.
 */
protected def getPreferredLocations(split: Partition): Seq[String] = Nil

/** Optionally overridden by subclasses to specify how they are partitioned. */
@transient val partitioner: Option[Partitioner] = None
5、首选位置 可选 计算数据时,可通过不同节点的状态决定计算节点位置 
/**
 * Optionally overridden by subclasses to specify placement preferences.
 */
protected def getPreferredLocations(split: Partition): Seq[String] = Nil

2、RDD 执行原理 

  从计算的角度来讲,数据处理过程中需要计算资源(内存&CPU)和计算模型(逻辑)。执行时,需要将计算资源和计算模型进行协调和整合。Spark框架在执行时,先申请资源,然后将应用程序的数据处理逻辑分解成一个一个的计算任务。然后将任务发到已经分配资源的计算节点上,按照指定的计算模型进行数据计算。最后得到计算结果。RDD是Spark框架中用于数据处理的核心模型,接下来我们看看,在Yarn环境中,RDD的工作原理

 1、启动 Yarn

 2、Spark 通过申请资源创建调度节点和计算节点

 3、spark 框架根据将计算逻辑根据计算需求分区划分成不同的任务;

4、调度节点将任务根据计算节点状态发送到对应的计算节点进行计算

 

从以上流程可以看出RDD在整个流程中主要用于将逻辑进行封装,并生成Task发送给Executor节点执行计算,接下来我们就一起看看Spark框架中RDD是具体是如何进行数据处理的。

 3、RDD的创建

 1、读取外部文件创建RDD

启动 spark-shell 

cd /opt/module/spark_local
[hui@hadoop103 spark_local]$ bin/spark-shell 

读取外部文件创建RDD


scala> val line=sc.textFile("README.md");
line: org.apache.spark.rdd.RDD[String] = README.md MapPartitionsRDD[1] at textFile at :24

scala> line.first();
res0: String = # Apache Spark

scala> line.count();
res1: Long = 108

这里读取了自带的 一个 README.md 文档, lines.count() 返回了 文档里的行数,lines.first() 返回了第一行

我们看下 sc 是什么

scala> sc
res3: org.apache.spark.SparkContext = org.apache.spark.SparkContext@2dcbf825
scala> lines
res4: org.apache.spark.rdd.RDD[String] = README.md MapPartitionsRDD[1] at textFile at :24

这里的 sc 就是 spark-shell 启动时创建的上下文环境对象。lines 是我们读取文件创建的RDD对象。

 IDEA 中读取外部文件创建RDD

object Spark01_RDD_file {
  def main(args: Array[String]): Unit = {
    //todo 准备环境
    val spekConf = new SparkConf().setMaster("local[*]").setAppName("RDD") //[*] 表示当前系统最大可用CPU核数
    val sc = new SparkContext(spekConf)
    //todo 创建RDD
    //文件中创建RDD,经文件数据作为spark处理数据的源头
    // path 路径默认以当前环境的根路径为基准,可用写绝对路径,也可以写相对路径
    // //val rdd: RDD[String] = sc.textFile("dates/1.txt")
    // path 可用是具体路径,直接读取路径下的所有文件
    //val rdd = sc.textFile("dates")
    // path 可用是具体路径,+ 通配符
    val rdd = sc.textFile("dates/1*.txt")
    rdd.collect().foreach(println)
    sc.stop()
  }
}

2、读取集合数据创建RDD

scala> val input = sc.parallelize(List(1,2,3,4,4))
input: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at :24

scala> val mapRDD = input.map(x=>x*2)
mapRDD: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[1] at map at :26

scala> println(mapRDD.collect().mkString(","))
2,4,6,8,8  

这里我们通过一个简单的list 来创建RDD,并调用了 map 方法对集合做了一个操作。

 IDEA 读取集合数据创建RDD

object Spark01_RDD_Memory {
  def main(args: Array[String]): Unit = {
    //todo 准备环境
    val spekConf = new SparkConf().setMaster("local[*]").setAppName("RDD") //[*] 表示当前系统最大可用CPU核数
    val sc = new SparkContext(spekConf)
    //todo 创建RDD
    //内存中创建RDD,经内存数据作为spark处理数据的源头
    val seq = Seq[Int](1, 2, 3, 4)
    //parallelize 并行
    // val rdd = sc.parallelize(seq)
    // makeRDD 底层调用了 parallelize  方法
    val rd  = sc.makeRDD(seq)
 
   /*
    def makeRDD[T: ClassTag](
    seq: Seq[T],
    numSlices: Int = defaultParallelism): RDD[T] = withScope {
    parallelize(seq, numSlices)  
   */
    rd.foreach(println) 
      /*
   def collect(): Array[T] = withScope {
  val results = sc.runJob(this, (iter: Iterator[T]) => iter.toArray)
  Array.concat(results: _*)
}
   */
    sc.stop()//todo 关闭环境
  }
}