Spark Core

Spark Core

Spark Core 简介:

  • Spark Core中封装了RDD的API,实现了高效的DAG执行引擎。

RDD 弹性分布式数据集:

  • RDD是spark中最基本的数据抽象,是一个抽象类,里面存储了用来获取数据的逻辑,并没有存数据。

RDD 特性:

  1. '一组分区':是数据集的基本组成单位,用来标记数据是那个分区的。

  2. '一个计算每个分区的函数':分区之间独立计算,并行执行,互不干扰,每个分区都要执行一次RDD逻辑。

  3. 'RDD之间的依赖关系' : RDD之间存在着依赖关系和血缘关系。

  4. '一个分区器': 是RDD的分片函数,用来控制分区数据的流向。

  5. '一个列表' :存取每个分区的优先位置,移动数据不如移动计算,除非资源不够。

    说明:
    1.RDD中封装的逻辑是不可变的,当一个RDD创建后,只能通过调用算子来获取另外一个RDD。
    2通过集合创建RDD如果不指明分区,默认会根据运行模式获取分区数,默认local[
    ]为当前CPU最大核心数, Local[K] K个核心数,Local 单线程1个核心数。
    3通过外部文件创建RDD的方式如果不指明分区,默认为两个分区,指明分区为最小的分区个数,而不是分区数。
    4Spark读取文件用的是hadoop1.X的API。
    5读取sequence、Object文件时时都要指明文件中数据的泛型,TextFile不需要。

创建RDD的方式:

  1. 通过集合创建,Parallelize(Arr)、MakrRdd(Arr)。

    说明:

    1.如果不指明分区数,默认当前CPU的总核心数,底层是totalCores。

    2.如果指明分区,每个分区的数据:

    length = 集合的长度 numSlices = 分区的个数  (0 until numSlices) => i
    Start=((i * length) / numSlices).toInt
    End = (((i+1) * length) / numSlices).toInt    =>(start,end) => 每个分区对应的数据[start,end)。
    
  2. 通过外部文件创建,如果没有指明参数,默认2个分区,Min(2,totalCorse),如果指明了,会对文件进行切分。

  3. 通过调用转换算子由其他的RDD转换而来。

算子:

  • 将问题的初始化状态通过操作进行转换为中间状态,然后再通过操作转换为完成状态。

    1. Driver:算子以外的代码都是在Driver端执行

    2. Executor:算子里面的代码都是在Executor端执行

    See more information about Spark 常用算子

序列化:

  • Driver和Executor位于不同的节点,Driver向Executor发送数据是会进行网络传输和进程通信,如果自定义类型时需要序列化,否则无法传输。
  • 序列化方式:
    1. 继承Serializable。
    2. 声明为样例类,默认继承Serializable。

闭包检查:

  • 通过RDD调用算子时,可能会用到Driver端的变量,而Spark自带闭包检查,并且在闭包检查的过程中会检查是否序列化,有闭包就需要进行序列化。

  • Kryo序列化:Java序列化方式比较重,Spark2.0 开始支持另外一种Kryo序列化机制。Kryo速度是Serializable的10倍。

    说名:使用Kryo序列化需要在SparkConf文件中设置替换默认的序列化机制以及注册需要使用kryo序列化的自定义类。

RDD血缘依赖:

  • 血缘:

    用来记录RDD之间的血缘关系,即RDD的由来,Lineage会记录RDD的元数据信息和转换关系,当RDD的部分分区数据丢失时,可以根据血缘关系重新运算和恢复分区数据。通过toDebugString算子可以查看RDD的血缘关系。

  • 依赖:

    用来记录不同RDD之间分区的关系。RDD的依赖类型有两种:

    1. NarrowDependency 窄依赖:RDD的一个分区或者多个分区的数据对应另外一个RDD的一个分区。
    2. ShuffleDependency 宽依赖:RDD的一个分区种的数据分配到另一个RDD的多个分区。
      说明:宽依赖会走Shuffle,要落盘会影响性能,在不影响业务要求的情况下,要尽量避免使用有宽依赖的转换算子。

Stage 任务划分:

  1. Application:初始化一个SparkContext即生成一个Application。

  2. Job:每一个Action算子就会生成一个Job。

  3. Stage:Stage等于宽依赖的个数加1。

  4. Task:在一个Stage阶段中,最后一个RDD的分区个数就是Task的个数。

    注意:Application->Job->Stage->Task每一层都是1对n的关系。

  • DAGScheduler:对job进行切分Stage,Stage产生Task。
  • ResultStage:往前先找宽依赖,找完为止,然后对宽依赖切,最后一个Stage还叫ResultStage,之前的叫ShuffleMapStage。
  • TaskScheduler(任务调度):通过TaskSet获取job的所有Task,然后序列化发送到Executor。

RDD 持久化:

  • 为了保证RDD数据的安全和高效,RDD通过cache算子或persist算子将计算结果进行提前保存,默认将数据以序列化的形式缓存到JVM内存中,这两个算子都是转换算子,遇到行动算子才会执行。当某个RDD的数据丢失时如果之前有缓存就不用在通过血缘关系重新从头执行,只需要读取缓存即可。

  • Cache与Persist的区别:

    Cache不支持修改缓存级别,缓存级别为MemoryOnly, 而Persist支持修改缓存级别,这两个算子都会增加血缘关系且不改变原有的血缘关系,而自带缓存的算子不会增加血缘关系。Unpersis可以释放缓存。

    **说明:缓存有可能丢失,或者存储于内存的数据由于内存不足而被删除,RDD的缓存容错机制保证了即使缓存丢失也能保证计算的正确执行。通过基于RDD的一系列转换,丢失的数据会被重算,由于RDD的各个Partition是相对独立的,因此只需要计算丢失的部分即可,并不需要重算全部Partition。Spark会自动对一些Shuffle操作的中间数据做持久化操作,这样做的目的是为了避免当一个节点Shuffle失败而是整个任务重新计算。 **

  • CheckPoint检查点:

    由于血缘依赖过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果检查点之后有节点出现问题,可以从检查点开始重做血缘,减少了开销。检查点需要设置结果的存储路径,可以为本地路径也可以为HDFS路径。

    说明:1.检查点会将当前RDD所有依赖于父RDD中的信息全部删除。
    2.CheckPoint也必须碰到行动算子才会执行,并且每一次CheckPoint为避免本次结果错误的可能,会隐藏的重新在启一个Job重新进行一次计算,并将这次执行的结果缓存。为了避免CheckPoint每次都会重新启动一个Job再执行一次任务,可以配合Cache使用,必须再CheckPoint之前cache。检查点存储的文件是分区文件,每个分区对应一个分区文件。

键值对RDD分区:

  • Spark只提供了两个分区:Hash分区(默认)和Range分区, 同时支持用户自定义分区。

    分区器直接决定了RDD中分区的个数、RDD中每条数据经过Shuffle后进入哪个分区和Reduce的个数。

    注意:

    1.只有Key-Value类型的RDD才有分区器,非Key-Value类型的RDD分区的值是None,但是有分区。

    2.只有KV的类型的RDD才有分区器是因为Spark只能按照Key来分区。

    3.每个RDD的分区ID范围:0~numPartitions-1,决定这个值是属于那个分区的。

  • Spark分区器:

  1. HashPartitioner:按照key的hash值%分区个数来获取分区,只能保证分区内有序不能保证全局有序。

  2. RangerPartitioner:区内有序,区间也有序。实现原理是先通过水塘抽样 算法来确定分区的范围,然后遍历数据集与分区范围做比较,然后将数据分配到指定的分区,这保证了数据在区间有序,然后使用SortBykey对区内数据进行排序,保证分区内数据有序,综上实现全局有序。

    说明:如果跑MR任务要保证全局有序,只需要有一个reduce即可。MR只有一个哈希分区器。

累加器:

  • 普通的变量只能从Driver端传到Executor端进行计算,但不能从Executor端再返回来,返回来的是个空值。而累加器用来把Executor端变量信息聚合到Driver端,在Driver中定义的一个变量,在Executor端的每个task都会得到这个变量的一份新的副本,每个task更新这些副本的值后,传回Driver端进行合并计算。

    说明:累加器是分布式共享只写变量,在Executor端只能写,读出来的数据不准确,因为读的只是当前分区的数据而并没有聚合所有分区的数据。

广播变量:

  • 广播变量用来高效分发较大的对象,向所有工作节点发送一个较大的只读值,以供一个或多个Spark Task操作使用。

  • 普通变量从Driver端发送的Executro端时,每个Task任务都会发送一份,数据量大时会浪费大量的内存空间。而广播只会向每个Executor发送一次,每个Executor上的Task共享这个广播变量。

    对分布式只对变量修改不起作用只会修改当前Executor端的值,不会改变其他Executor的广播变量。写Hive是用orderBy即可。

posted @ 2021-05-31 15:33  yuexiuping  阅读(77)  评论(0编辑  收藏  举报