Spark数据结构:RDD

目录

一、RDD概述

        1.RDD定义

        2.RDD设计的核心点:

        3.RDD的特性

二、RDD创建

        (1)创建RDD

        (2)RDD分区数目

三、RDD函数

        (1)函数分类

一、RDD概述

        1.RDD定义

        RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,代表一个不可变、可分区、里面元素可并行计算的集合。

        ·Dataset:一个数据集合,用于存放数据的。

        ·Distributed:RDD中的数据是分布式存储的,可用于分布式计算。

        ·Resilient:RDD中的数据可以存储在内存中或者磁盘中。

       可以认为RDD是分布式的列表List或数组Array,抽象的数据结构,RDD是一个抽象类Abstract Class和泛型Generic Type。

       RDD将Spark的底层的细节都隐藏起来(自动容错、位置感知、任务调度执行,失败重试等),让开发者可以像操作本地集合一样以函数式编程的方式操作RDD这个分布式数据集,进行各种并行计算,RDD中很多处理数据函数与列表List中相同与类似。

4a29572a9fb846fcbdcdbda17300a846.png

        2.RDD设计的核心点:

       RDD提供了一个抽象的数据模型,不必担心底层数据的分布式特性,只需将具体的应用逻辑表达为一系列转换操作(函数),不同RDD之间的转换操作之间还可以形成依赖关系,进而实现管道化,从而避免了中间结果的存储,大大降低了数据复制、磁盘IO和序列化开销,并且还提供了更多的APl(map/reduec/filter/groupBy等等)。

c78d71350f574f28b3d68f698ff9558d.png

        3.RDD的特性

        (1)A list of partitions(RDD是有分区的)

        ①一组分片(Partition)/一个分区(Partition)列表,即数据集的基本组成单位;

        ②对于RDD来说,每个分片都会被一个计算任务处理,分片数决定并行度;

        ③用户可以创建RDD是指定RDD的分片个数,如果没有指定,那就会采用默认值;

        (2)A function for computing each split(计算方法会作用带每一个分区(分片)之上)

        ①一个函数会被作用在每一个分区;

        ②Spark中RDD的计算是以分片为单位的,compute函数会被作用到每个分区上;

        (3)A list of dependencies bn other RDDs(RDD之间是有相互依赖的关系的)

        ①一个RDD会依赖于其他多个RDD;

        ②RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算(Spark的容错机制)。

     (4)Optienally, a Partitioner for key-value RDDs (e.g. to say that the RDD ishash-partitioned)(KV型RDD可以有分区器)

        ①可选项对于KeyValue类型的RDD会有一个Partitioner,即RDD的分区函数;

        ②当前Spark中实现了两种类型的分区函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。

       ③只有对于于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。

        ④Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。

     (5)Optionally, a list of preferred locations to compute each split on (e.g. block locationsfor an HDFS file)(RDD分区数据的读取会尽量靠近数据所在地)

        ①可选项,一个列表,存储存取每个Partition的优先位置(preferred location);

        ②对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。

        ③按照"移动数据不如移动计算"的理念,Spark在进行任务调度的时候,会尽可能选择那些存有数据的worker节点来进行任务计算。(数据本地性)

        RDD是一个数据集的表示,不仅表示了数据集,还表示了数据集从哪来、如何计算,主要属性包括五个方面(必须牢记,通过编码加深理解,面试常问):

3264ada3f1454d2784cdf1fe1af3e799.png

        RDD 设计的一个重要优势是能够记录 RDD间的依赖关系,即所谓血统(lineage)。通过丰富的转移操作(Transformation),可以构建一个复杂的有向无环图,并通过这个图来一步步进行计算。

wordcount代码RDD特性分析

二、RDD创建

        (1)创建RDD

        将数据封装到RDD集合中,主要有两种方式:并行化本地集合(Driver Program中)和引用加载外部存储系统(如HDFS、Hive、HBase、Kafka、Elasticsearch等)数据集。

/* SparkApplication应用,创建SparkContext对象,及应用结束关闭资源,代码示例*/

def main(args: Array [string]): Unit = {

    //构建SparkContext对象
    val sc : SparkContext = {
        // a.创建SparkConf对象
        import org.apache.spark.{SparkConf,SparkContext}
        val sparkConf = new SparkConf()
            .setAppName(this.getClass.getSimpleName.StripSuuffix("$"))
            .setMaster("local[2]")
        //b.传递SparkConf对象,创建实例
        val context = SparkContext.getOrCreate(sparkConf)  //有就获取,没有创建
        //c.返回实例对象
        context
    }
    //TODO:创建本地集合
    val seq:Seq[Int] = Seq(1, 2, 3, 4, 5, 6, 7, 8)
    
    //并行化本地集合,创建RDD
    /*
        def parallelize[T: ClassTag](
            seq: Seq[T],
            numSlices: Int = defaultParallelism
        ): RDD[T]
    */
    val inputRDD:RDD[Int] = sc.parallelize(seq,numSlices = 2)
    inputRDD.foreach(item => println(item))

    //TODO:外部存储系统
    /*
        def textFile(
            path: string,
            mihPartitions: Int = defaultMinPartitions
    ): RDD[String]
    */
    sc.textFile( path = "datas/wordcount.data", minPartitions = 2)
    //其中文件路径:最好是全路径,可以指定文件名称,可以指定文件目录,可以使用通配符指定。
    //实际项目中如果从HDFS读取海量数据,应用运行在YARN上,默认情况下,RDD分区数目等于HDFS上Block块数目。


    //应用结束,关闭资源
    sc.stop()
}
        (2)RDD分区数目

        分区是一个偏物理层的概念,也是RDD并行计算的核心。数据在RDD内部被切分为多个子集合,每个子集合可以被认为是一个分区,运算逻辑最小会被应用在每一个分区上,每个分区是由一个单独的任务(task)来运行的,所以分区数越多,整个应用的并行度也会越高。

/*获取RDD分区数目两种方式:*/
    //方式一:直接获取
    rdd.getNumPartitions
    //方式二:通过RDD获取
    rdd.partitions .length

        RDD分区的数据取决于哪些因素?

        ①RDD分区的原则是使得分区的个数尽量等于集群中的CPU核心(core)数目,这样可以充分利用CPU的计算资源;

        ②在实际中为了更加充分的压榨CPU的计算资源,会把并行度设置为cpu核数的2~3倍;

        ③RDD分区数和启动时指定的核数、调用方法时指定的分区数、如文件本身分区数有关系,具体如下说明:

       (1)、启动的时候指定的CPU核数确定了一个参数值:

                spark.default.parallelism=指定的CPU核数(集群模式最小2).

       (2)、对于Scala集合调用parallelize(集合,分区数)方法

                如果没有指定分区数,就使用spark.default.parallelism

                如果指定了就使用指定的分区数(不要指定大于spark.default.parallelism)

        (3)、对于textFile(文件,分区数)

        defaultMinPartitions:

                如果没有指定分区数sc.defaultMinPartitions=min(defaultParallelism,2)

                如果指定了就使用指定的分区数sc.defaultMinPartitions=指定的分区数rdd的分区数

        RDD的分区数

        (1)对与本地文件

        rdd的分区数= max(本地file的分片数,sc.defaultMinPartitions)

        (2)对于HDFS文件

        rdd的分区数= max(hdfs文件的block数目,sc.defaultMinPartitions)

        所以如果分配的核数为多个,且从文件中读取数据创建RDD,即使hdfs文件只有1个切片,最后的Spark的RDD的partition数也有可能是2。

三、RDD函数

        (1)函数分类

        对于Spark处理的大量数据而言,会将数据切分后放入RDD作为Spark的基本数据结构,开发者可以在 RDD上进行丰富的操作,之后Spark 会根据操作调度集群资源进行计算。总结起来,RDD 的操作主要可以分为Transformation Action 两种。

        ①Transformatioi转换操作:返回一个新的RDD

                ·从现有数据集创建新数据集。

                ·所有Transformation函数都是Lazy,不会立即执行,需要Action函数触发。

        ②Action动作操作:返回值不是RDD(无返回值或返回其他的)

                ·在数据表上运行计算后向驱动程序返回一个值。

                ·所有Action函数立即执行(Eager),比如count、first、collect、take等。

8f7b442e0e584463a7970ac3586da92f.png

56f3ae4a8fdc4b46aab55cd3ae5e0272.png

        RDD中函数细节:

        ①RDD不实际存储真正要计算的数据,而是记录了数据的位置在哪里,数据的转换关系(调用了什么方法,传入什么函数);

        ②RDD中所有的转换都是惰性求值/延迟执行的,也就是说并不会直接计算。只有发送一个要求返回结果给Driver的Action动作时,这些转换才会真正运行。之所以使用惰性求值/延迟执行,是因为这样可以在Action时对RDD操作形成DAG有向无环图进行Stage的划分和并行优化,这种设计让Spark更加有效率地运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吗喽也是命

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值