spark总结01

34 篇文章 1 订阅
32 篇文章 1 订阅

目录

第一部分:SaprkCore部分

Spark简介

1、什么是RDD? RDD的5大特性。

2、怎么理解partition,如何合理的设置partition的数量。

3、RDD或者partition里面存储数据吗?怎么理解内存计算。

4、Spark中的hello world (word count)

5、Spark架构原理

6、创建初始的RDD

7、RDD算子操作

8、RDD持久化操作

9、RDD共享变量

10、Spark核心编程高级编程之基于排序机制的wordcount程序

11、Spark核心编程高级编程之二次排序

12、Spark分组取TopN

13、Spark内核源码深度剖析Spark内核架构深度剖析

14、SparkContext原理剖析和源码分析(了解)

15.Job触发流程原理剖析和源码分析

16、Shuffle原理剖析和源码分析

17、BlockManage原理剖析

 18、checkPoint算子

19、Spark性能优化

20、Spark源码编译

第二部分:SparkSQL部分

1、SparkSQL程序主入口

2、创建DataFrame的常用的方式

3、DataFrame的常用操作

4、为什么将RDD转为DataFrame?以及如何将RDD转为DataFrame?

5、Spark通用的load和save操作、Save Mode保存模式

6、parquet文件

7、Json文件

8、Hive数据源实战

9、JDBC数据源实战

10、SparkSQL内置函数的使用

11、SparkSQL UDF 自定义函数的使用

12、SparkSQL UDAF 自定义聚合函数的使用

13、SparkSQL 性能优化

14、核心源码深度剖析

15、案例实战 


第一部分:SaprkCore部分

Spark简介

详细介绍链接:https://blog.csdn.net/yangshaojun1992/article/details/78336109

1、什么是RDD? RDD的5大特性。

    RDD是spark中的一种抽象,他是弹性分布式数据集.

    a) RDD由一系列的partition组成

    b) 算子作用在partition上

    c) RDD之间具有依赖关系

    d) partition提供了最佳计算位置(体现了移动计算不移动数据思想)

    e) 分区器作用在K、V格式的RDD上。

 哪里体现了弹性、分布式?

    弹性:partition可大可小,可多可少。RDD之间具有依赖关系;能做到很好的容错。

    分布式:partition分布在集群的不同节点上。

2、怎么理解partition,如何合理的设置partition的数量。

      在HDFS存储的数据是以block块的形式进行物理存储。RDD是Spark加载HDFS数据到内存中的抽象,里面不存储数据。RDD是由partion组成的,partion被分布在多个节点上。partion是数据的逻辑存储,实际上不存储数据集。当我们对RDD进行操作时,实际上是对每个分区中的数据并行操作。

    Spark从HDFS读入文件的分区数默认等于HDFS文件的块数(blocks),HDFS中的block是分布式存储的最小单元。如果我们上传一个30GB的非压缩的文件到HDFS,HDFS默认的块容量大小128MB,因此该文件在HDFS上会被分为235块(30GB/128MB);Spark读取SparkContext.textFile()读取该文件,默认分区数等于块数即235。

  如何设置合理的分区数

1、分区数越多越好吗?

不是的,分区数太多意味着任务数太多,每次调度任务也是很耗时的,所以分区数太多会导致总体耗时增多。

2、分区数太少会有什么影响?

分区数太少的话,会导致一些结点没有分配到任务;另一方面,分区数少则每个分区要处理的数据量就会增大,从而对每个结点的内存要求就会提高;还有分区数不合理,会导致数据倾斜问题。

3、合理的分区数是多少?如何设置?

总核数=executor-cores * num-executor

一般合理的分区数设置为总核数的2~3倍

3、RDD或者partition里面存储数据吗?怎么理解内存计算。

  RDD或者partition里面不存储数据,partition只是数据的逻辑存储。实际存储数据的地方在block中。

  在RDD依赖关系中,在没有action算子触发之前,RDD之间的依赖只是一种逻辑关系,不会触发真正的计算

 当action算子触发后数据进行计算,计算的中间结果存储在内存中。

4、Spark中的hello world (word count)

    使用java、scala和spark-shell开发wordCount程序。 spark-shell 其实就是spark的命令行,方便测试,验证程序

   java开发


/**
 * 本地测试wordCount
 *
 * @author yangshaojun
 * #date  2019/3/9 10:00
 * @version 1.0
 */
public class Demo000_WordCountLocal {
    public static void main(String[] args) {
        //  编写spark应用程序
        //  第一步:创建sparkconf对象,设置spark应用配置信息
        //  使用setMaster()设置Spark应用程序要连接的Spark集群的Master节点的url
        //  如果设置local则代表在本地执行
        SparkConf conf = new SparkConf().setAppName("wordCount").setMaster("local[2]");
        //  第二步: 创建JavaSparkContext对象
        //  在Spark中SparkContext是spark所有功能的入口,无论使用java、scala还是python编写都必须有一个sparkContext对象.
        //  它的作用:初始化spark应用程序所需要的的一些核心组件,包括调度器(DAGSchedule、TaskSchedule)
        //  还回去spark Master节点上进行注册等等。
        //  但是呢,在spark中编写不同类型的spark应用程序使用的spakContext是不同的,
        //  如果使用scala那么就是sparkContext对象
        //  如果使用java那么就是JavaSparkContext对象
        //  如果开发spark Sql程序那么就是SQLContext或者HiveContext
        //  如果是开发Spark Streaming程序,那么就是它独有的SparkContext。
        JavaSparkContext sc = new JavaSparkContext(conf);
        //  第三步:从数据源(HDFS、本地文件等)加载数据 创建一个RDD
        //  parkContext中根据文件的类型输入源创建的RDD的方法 是textFile()
        //  在java中,创建的普通RDD都叫做JavaRDD
        //  在这里呢,RDD有元素的概念,如果是HDFS或者是本地文件创建的RDD,其每个元素相当于文件的一行数据。
        JavaRDD<String> linesRDD = sc.textFile("data/sparkcore/wordcount");
        //  第四步: 对初始的RDD进行transformation操作
        //  将每一行拆分成单个的单词
        JavaRDD<String> words = linesRDD.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterable<String> call(String line) throws Exception {
                String[] str = line.split(",");
                return Arrays.asList(line.split(","))
            }
        });
        JavaPairRDD<String, Integer> word = words.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) {
                return new Tuple2<String, Integer>(s, 1);
            }
        });
        JavaPairRDD<String, Integer> result = word.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer int1, Integer int2) throws Exception {
                return int1 + int2;
            }
        });

        result.foreach(new VoidFunction<Tuple2<String, Integer>>() {
            @Override
            public void call(Tuple2<String, Integer> stringIntegerTuple2) throws Exception {
                System.out.println(stringIntegerTuple2);
            }
        });


    }
}

   提交spark程序到集群运行

  Note:

   1)如果要在spark集群上运行应用程序,需要将本地开发中的 setMaster()删除默认自己连接,同时将本地文件改为      hdfs文件。

   2)对Spark应用程序打成jar包,上传到服务器上

   3)编写spark-submit脚本、执行spark-submit脚本、提交应用程序到集群执行。

 ./spark-submit 
--class com.netcloud.bigdata.spark_core.basiclearning.demo.Demo000_WordCount 
--num-executor 3 \
--driver-memory 100m \
--executor-memory 100m \
--executor-cores 3 \
/opt/wordcount.jar

Note:在这里我们提交程序的时候没有加 --master spark://192.168.2.100:7077 表示任务的执行是在local模式下执行的。

      否则就是在standalone模式下执行。

scala开发

object Demo000_WordCount {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("word_count").setMaster("local[2]")
    val sc = new SparkContext(conf)
    val linesRDD = sc.textFile("data/sparkcore/wordcount.txt")
    val wordsRDD = linesRDD.flatMap(line => line.split(","))
    val wordTuple = wordsRDD.map(word => (word, 1))
    val retRdd = wordTuple.reduceByKey((a: Int, b: Int) => (a + b))
    retRdd.foreach(println)
  }

}

 

  spark-shell开发(主要用于简单的测试)

深度剖析wordCount

Note:reduceBykey算子产生shuffle,什么是shuffle?shuffle就是数据重新进行分区、落地到磁盘的过程。

5、Spark架构原理

--  Application:用户编写的Spark应用程序。

--  Driver : Driver就是一个进程,我们编写的spark应用程序就在Driver上面;由Driver进程执行。根据任务提交的方式不同Driver所处的节点位置不同;Driver可能处在我们提交spark程序的机器上面,也有可能处在spark集群中的某个节点之上。

--  Master: 它是Spark集群的主进程;负责负责资源的调度和分配、和集群的监控。

-- Worker: 它是Spark集群的进程;通过自己的内存对partition进行存储,启动其他的进程或者线程对Rdd中的partition进行计算。

-- Executor:Executor是work节点的进程,在Executor上面可以启动多个Task线程。

-- Task:在Executor进程上面启动的线程。

Note:

Executor和Task其实就是负责执行,对RDD中的partition进行并行的计算,也就是执行我们对RDD定义的map、flatMap算子操作。

6、创建初始的RDD

  1)通过程序中集合的方式创建RDD、主要用于测试使用。 通过parallelizer()方法

  2)使用本地文件创建RDD;主要是临时性处理一些存储大量的文件 textFile(path)方法可以支配到目录加载整个目录的文件。

  3)使用HDFS文件创建RDD;主要用于生产环境;进行离线批处理。 textFile(path)方法

7、RDD算子操作

  转换和行动算子。

 转换算子:它是懒执行的,当真正的触发行动算子后才进行之前的转换计算。

 行动算子:触发真正spark job的操作。 reduce 、collect 、count、first、take、countByKey()、takeSample、foreach、save

 等9种行动算子。

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88376322

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88376963

https://blog.csdn.net/yangshaojun1992/article/details/78358801

8、RDD持久化操作

不使用RDD持久化带来的问题:

持久化后的情况:

代码实例链接:https://blog.csdn.net/yangshaojun1992/article/details/88377557

9、RDD共享变量

共享变量的原理

广播变量:

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88378882

 

累加器:

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88378882

10、Spark核心编程高级编程之基于排序机制的wordcount程序

   textFile - > flatMap -> map -> reduceByKey -> map -> sortByKey -> map -> collect 

   代码示例链接:https://blog.csdn.net/yangshaojun1992/article/details/88538117

11、Spark核心编程高级编程之二次排序

* 1) 实现自定义的Key,要实现Ordered接口和Serializable接口,在key中实现自己对多个列的排序算法。
* 2) 将包含文本的RDD,映射成Key为自定义的key、value为文本的RDD。
* 3) 使用SortByKey算子 按照自定义的Key进行排序。
* 4) 再次映射,剔除自定义的key,只保留文本行。

示例代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88563547

12、Spark分组取TopN

1、对文本文件中的数字,获取最大的前三个。

2、对每个班级内的学生成绩,取出前三名。(分组TopN)

代码示例链接:https://blog.csdn.net/yangshaojun1992/article/details/88578588

13、Spark内核源码深度剖析Spark内核架构深度剖析

对以上的内容如果全部掌握,只能算是熟练,谈不上精通,当对源码就行透彻的研究之后,才能算的上精通。

下面介绍的源码和内核的分析对于精通至关重要。

1、Application:就是自己编写的spark应用程序。

2、spark-submit:结合shell脚本 使用spark-submit命令提交 Application。

3、Driver:当spark应用程序提交的方式是client的方式的时候,会在提交Application的那台机器上启动一个Driver进程。

a) 这个Driver进程会去执行我们编写的Application应用程序,也就是我们自己书写的代码,执行的时候首先创建一个SparkContext。

b) 当SparkContext初始化的时候,做的最重要的两件事就是构造出 DAGScheduler和TaskScheduler。(也就是说sparkContext包含DAGScheduler和TaskScheduler)

c) TaskScheduler(通过自己的后台进程)负责去连接spark集群中的Master,接着向Master发送注册Application请求,Master接收到注册请求后注册application并且会使用自己的资源调度算法,去连接哪些worker,然后让这些可用的worker,为这个Application启动多个Executor进程(一个worker节点可以启动一个Executor也可以启动多个Executor进程)。

d) Executor进程启动之后,自己会反向注册到TaskScheduler。

当所有的 Executor进程启动了,并且自己都会反向注册到TaskScheduler上了。也就意味着Driver结束SparkContext的初始化了,会继续执行我们自己编写的代码。

a)当Driver每次执行到action,就会创建一个job,job会提交给DAGScheduler。

DAGScheduler会将job划分为多个stage(stage划分算法),每个stage创建一个TaskSet,然后每个TaskSet会给到TaskScheduler,TaskScheduler这个时候会把TaskSet中的每个task提交到Executor上执行(task算法)。

b) Executor(内部有一个线程池),每接收到一个task 都会有TaskRunner来封装task,然后从线程池里面取出一个线程来执行这个task。TaskRunner:将我们编写的代码,也就是要执行的算子以及函数,拷贝、反序列化、然后执行task。

c) task有两种:shuffMapTask和ResultTask,只有最后一个stage是ResultTask,之前的stage都是shuffMapTask。

d) 最后整个spark应用程序的执行,就是stage分作为TaskSet提交到executor执行,每个task针对RDD中的一个partiton执行我们的算子和函数,以此类推知道所有的操作执行完为止。

 13.1 Spark中的宽窄依赖

 使用wordcount例子讲解:

详细链接:https://blog.csdn.net/yangshaojun1992/article/details/78441935 

13.2 Spark Application应用程序提交模式 

执行流程

1. 客户端提交一个Application,在客户端启动一个Driver进程。

2. 应用程序启动后会向RS(ResourceManager)发送请求,启动AM(ApplicationMaster)的资源。

3. RS收到请求,随机选择一台NM(NodeManager)启动AM。这里的NM相当于Standalone中的Worker节点。

4. AM启动后,会向RS请求一批container资源,用于启动Executor.

5. RS会找到一批NM返回给AM,用于启动Executor。

6. AM会向NM发送命令启动Executor。

7. Executor启动后,会反向注册给Driver,Driver发送task到Executor,执行情况和结果返回给Driver端。

总结

Yarn-client模式同样是适用于测试,因为Driver运行在本地客户端,负责调度application,Driver会与yarn集群中的Executor进行大量的通信,会造成客户端网卡流量的激增。好处就是在本地可以看到所有的log,方便测试。

ApplicationMaster的作用:

1. 为当前的Application申请资源

2. 给NodeManager发送消息启动Executor。

注意:ApplicationMaster申请资源的功能,并没有作业调度的功能。

执行流程

1. 客户机提交Application应用程序,发送请求到RS(ResourceManager),请求启动AM(ApplicationMaster)。

2. RS收到请求后随机在一台NM(NodeManager)上启动AM(相当于Driver端)。

3. AM启动,AM发送请求到RS,请求一批container用于启动Excutor。

4. RS返回一批NM节点给AM。

5. AM连接到NM,发送请求到NM启动Excutor。

6. Excutor反向注册到AM所在的节点的Driver。Driver发送task到Excutor。

总结

Yarn-Cluster主要用于生产环境中,因为Driver运行在Yarn集群中某一台NodeManager中,每次提交任务的Driver所在的机器都是随机的,不会产生某一台机器网卡流量激增的现象,缺点是任务提交后不能看到日志。只能通过yarn查看日志。

ApplicationMaster的作用:

1. 为当前的Application申请资源

2. 给NodeManager发送消息启动Excutor。

3. 任务调度。

停止集群任务命令:yarn application -kill applicationID

13.3 Spark任务提交命令

Yarn-client:

提交命令

./spark-submit

--master yarn

 --class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

或者

./spark-submit

--master yarn–client

 --class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

或者

./spark-submit

--master yarn

--deploy-mode  client

 --class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

yarn-cluster
提交命令

./spark-submit

--master yarn

--deploy-mode cluster 

--class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

或者

./spark-submit

--master yarn-cluster

--class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100
 

Standalone-Client

提交命令

./spark-submit

--master spark://master节点ip:7077

--class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

Standalone-Cluster

./spark-submit

--master  spark://master节点ip:7077

--deploy-mode cluster 

--class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/78387918

14、SparkContext原理剖析和源码分析(了解

   14.1  SparkContext在进行初始化的时候创建 TaskScheduler和DagScheduler的原理过程剖析:

14.2 SparkContext初始化时创建TaskScheduler以及发送application注册请求源码分析:

创建过程详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88657946

14.3 Application注册到Master的原理和源码分析:

创建过程详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88657946

14.4 Application在Mastter上注册完成后(schedule方法执行)

当上面的整个过程已经执行完了(也就是说TaskScheduler如何创建、TaskScheduler如何向Master发送注册Application的请求,同时Master也如何完成对Application的注册);接着这里主要分析,Master是如何连接到集群可用的Worker,以及如何启动worker中的Executor进程的。

1)判断Master的状态,如果Master的状态不是active的话,直接返回、对于不是Active的Master 是不会进行资源调度的(连接可用的Master,启动Executor进程)。

2) Random.shuffle 方法将传入集合中的元素(将之前所有注册的workers信息)进行随机的打乱;同时返回集群中可用的worker(状态是alive的)

3)默认情况下,有多少个worker节点就会启动多少个Executor进程。但是我们也可以在提交application程序的时候指定Executor的数量。

在yarn模式下,直接指定 –num-executors 这个参数 。

或者使用多个参数共同决定 executor数目:

 executor 数量 = spark.cores.max/spark.executor.cores

  • spark.cores.max 是指你的spark程序需要的总核数
  • spark.executor.cores 是指每个executor需要的核数

或者:它们共同决定了当前应用 启动executor的个数

--total-executor-cores

--executor-cores

4)Task被执行的并行度

注意: 这里的core是虚拟的core而不是机器的物理CPU核,可以理解为就是Executor的一个工作线程。

而 Task被执行的并发度 = Executor数目 * 每个Executor核数。

至于partition的数目:

  • 对于数据读入阶段,例如sc.textFile,输入文件被划分为多少InputSplit就会需要多少初始Task。
  • 在Map阶段partition数目保持不变。
  • 在Reduce阶段,RDD的聚合会触发shuffle操作,聚合后的RDD的partition数目跟具体操作有关,例如repartition操作会聚合成指定分区数,还有一些算子是可配置的。

RDD在计算的时候,每个分区都会起一个task,所以rdd的分区数目决定了总的的task数目。
申请的计算节点(Executor)数目和每个计算节点核数,决定了你同一时刻可以并行执行的task。
比如的RDD有100个分区,那么计算的时候就会生成100个task,你的资源配置为10个计算节点,每个两2个核,同一时刻可以并行的task数目为20,计算这个RDD就需要5个轮次。
如果计算资源不变,你有101个task的话,就需要6个轮次,在最后一轮中,只有一个task在执行,其余核都在空转。
如果资源不变,你的RDD只有2个分区,那么同一时刻只有2个task运行,其余18个核空转,造成资源浪费。这就是在spark调优中,增大RDD分区数目,增大任务并行度的做法。

源码实例链接:https://blog.csdn.net/yangshaojun1992/article/details/88657946

15.Job触发流程原理剖析和源码分析

当SparkContext初始化完成以后,紧接着代码会往下继续执行(执行我们编写的spark应用程序)

1) 一个Application程序可能包含多个Job,而Job的划分是根据action操作划分的,(也就是说一个job对应着一个action,一个application包含多个action,也就意味着包含多个job)

2) 当一个application中包含多个job的时候,首先执行第一个job(第一个action操作),当第一个job运行完成以后,才会执行第二个job(第二个action操作)以此类推。 

根据wordCount进行讲解:

15.1 Stage划分算法原理剖析

 

1)像reduceByKey这样的操作(产生shuffle的算子),它其实会产生3个RDD。

2)因为我们要进行reduceByKey,所以一个key对应的那些value必须让一个task去获取处理。

3)因此这里我们首先使用hashpartitioner对数据进行重新分区,即:将每个节点中多个partition中含有相同key的数据分到同一个分区的本地磁盘文件中;产生一个MapPartitionRDD。

Note:(这个时候指定了多少个分区就会写多少个文件中)每个节点重新进行分区的数据,落地到磁盘后,相同的key一定在一个文件中,一个文件中也可能有多个key。 因此各个节点重新分区后的磁盘文件中可能都含有相同key的文件。

4)shuffleRDD的产生会涉及到网络的传输。它的每个partition必须从各个节点每个partition(相同key的分区文件)上去拉取分区文件。

5)最后的MapPartitionRDD将拉取过来的shuffleRDD按照Key进行聚合。

这里的shuffleRDD相对于第一个MapPartitionRDD就是宽依赖。也就是说第一个MapPartitionRDD就是宽依赖的RDD。

15.2 DAGScheduler源码分析(stage划分算法、task最佳位置计算算法)

源码实例链接:https://blog.csdn.net/yangshaojun1992/article/details/88652447

15.3 TaskScheduler提交TaskSets中的每个task到Executor执行源码分析

接着15.2最后一步继续分析:

TaskSetManager的作用(了解):

// 在TaskSchedulerImpl中,对一个单独的TaskSet的任务进行调度,TaskSetManager这个类负责追踪每个task
// 如果task失败的话,会负责重试task,直到超过重试的次数限制,并且会通过延迟调度,为这个TaskSet处理本地化调度机制。
// 它的主要接口就是resourceoffer ,在这个接口中,TaskSet会希望在一个节点上运行一个任务,并且接收任务的状态改变消息,
// 来知道他负责的task的状态改变了。

源码实例链接:https://blog.csdn.net/yangshaojun1992/article/details/88677237

15.4 Executor原理剖析与源码分析

源码分析链接:https://blog.csdn.net/yangshaojun1992/article/details/88753456

 15.5 Task原理剖析与源码分析

上面讲了task的执行是在一个线程池中并行的执行。

下面要讲的是,task是如何执行我们编写的算子的具体过程

 源码链接:https://blog.csdn.net/yangshaojun1992/article/details/88769295

16、Shuffle原理剖析和源码分析

1、spark中什么情况下产生shuffle()

reduceByKey、groupByKey,sortByKey、countByKey、join、cogroup等操作。

2、默认情况下的shuffle原理的源码分析

问题:聚合之前,每一个相同key对应的value不一定都是在一个partition中,partition也不太可能在同一个节点上,因为RDD是分布式的弹性的数据集,RDD的partition极有可能分布在各个节点上。

如何聚合?

– Shuffle Write:上一个stage的每个shuffleMapTask就必须保证(使用默认分区器hashPartitioner)将自己处理的当前partition中数据相同key的写入各自的磁盘文件中。

– Shuffle Read:ResultTask就会从shuffle Write产生的磁盘文件中(可能在不同的节点上)去通过网络传输的方式拉取,这样就可以保证每一个相同key所对应的那些value都会汇聚到同一个节点上去处理和聚合。

下面的原理图只是分析了在一个节点多个task的执行情形,实际上其他节点也同样会进行Shuffle Write。
 

 

总结:

执行流程

a) 每一个ShuffleMapTask将partition中不同Key对应的value数据写到不同的bucket中;bucket起到数据缓存的作用。

b) 每个bucket文件最后对应一个磁盘小文件。 (磁盘小文件的数目=集群task(或partition)的数目*ResultTask的数目)

c) ResultTask 通过网络传输来拉取对应的磁盘小文件。

带来的问题:

产生的磁盘小文件过多,会导致以下问题:

a) 在Shuffle Write过程中会产生很多写磁盘小文件的对象。

b) 在Shuffle Read过程中会产生很多读取磁盘小文件的对象。

c) 在JVM堆内存中对象过多会造成频繁的gc,gc还无法解决运行所需要的内存 的话,就会OOM。

d) 在数据传输过程中会有频繁的网络通信,频繁的网络通信出现通信故障的可能性大大增加,一旦网络通信出现了故障会导致shuffle file cannot find 由于这个错误导致的task失败,TaskScheduler不负责重试,由DAGScheduler负责重试Stage。

3、优化后的shuffle原理图(合并机制)

总结:产生磁盘小文件的个数:(集群中core的个数(并行度)) *(ResultTask的个数)

4、Shuffle源码分析(Shuffle Write 和 Shuffle Read)

代码实例链接:https://blog.csdn.net/yangshaojun1992/article/details/88778573

17、BlockManage原理剖析

BlockManageMaster在Driver节点上,而BlockManager在集群的其他节点上。

 18、checkPoint算子

控制算子有三种,cache,persist,checkpoint,以上算子都可以将RDD持久化,持久化的单位是partition。cache和persist都是懒执行的。必须有一个action类算子触发执行。checkpoint算子不仅能将RDD持久化到磁盘,还能切断RDD之间的依赖关系。

checkpoint 的执行原理:

1. 当RDD的job执行完毕后,会从finalRDD从后往前回溯。

2. 当回溯到某一个RDD调用了checkpoint方法,会对当前的RDD做一个标记。

3. Spark框架会自动启动一个新的job,重新计算这个RDD的数据,将数据持久化到HDFS上。

优化:对RDD执行checkpoint之前,最好对这个RDD先执行cache,这样新启动的job只需要将内存中的数据拷贝到HDFS上就可以,省去了重新计算这一步。
 

使用场景:

19、Spark性能优化

上面的Spark内核和下面将介绍的性能优化值得反复进行学习(重中之重)

1、性能优化的重要性

(Spark因为基于内存计算的;所以经常出现OOM、内部异常、文件丢失、task丢失等问题) 这个时候就需要对上述对的问题就索性优化操作。

 2、Spark的内存的消耗情况。

Spark在执行程序的时候内存都消耗在哪里了呢?

 3、判断程序消耗了多少内存

 Spark的性能优化主要是对内存进行优化。

4、将对下面的10项问题进行调优

 1)使用高性能的序列化类库 (Kryo)

数据序列化是为了对数据进行缓存到内存、持久化到磁盘以及进行网络的传输等。

spark默认使用的序列化机制是Java自身提供的序列化机制(这种默认的机制缺点就是序列化速度慢、序列化后的数据还是很大)

 2)优化数据结构

     优先使用数组而不是集合、避免多层自定义对象的嵌套、使用int类型的id替换string类型的id。

 3)对多次使用的RDD进行持久化或者checkpoint

 4)使用序列化的持久化级别

5)Java虚拟机垃圾回收调优

高级垃圾回收

 

 

 6)提高并行度

 

 7)广播共享数据

 8)数据本地化

 

 

  9)reduceByKey和groupBykey

 

10)shuffle性能优化

详细解释链接:https://blog.csdn.net/yangshaojun1992/article/details/78503651 

20、Spark源码编译

第二部分:SparkSQL部分

1、SparkSQL程序主入口

在spark1.x 使用的是SQLContext 和HiveContext
在Spark2.x之后统一使用sparkSession

2、创建DataFrame的常用的方式

从已经存在的RDD生成,从hive表、或者其他数据源(本地或者HDFS)

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

3、DataFrame的常用操作

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

4、为什么将RDD转为DataFrame?以及如何将RDD转为DataFrame?

RDD转为DataFrame 可以将HDFS上的任何构建RDD的数据使用SparkSQL进行SQL查询,此功能无比的强大,想象一下
针对HDFS上的数据,直接就可以使用SQL进行查询了。

SparkSQL支持两种形式将RDD转为DataFrame。
反射的方式
适合schema信息是确定的情形。
a) Case class 定义了表的 Schema。
b) Case class 的参数名使用反射读取并且成为了列名。 
c) Case class 也可以是嵌套的或者包含像 SeqS 或者 ArrayS 这样的复杂类型。

动态转换编写代码的方式:
适合schema信息不确定,需要动态的从mysql db或者配置文件加载的。通过编程的方式创建元数据。
a) 将从文本文件中创建的原始RDD(RDD[String])使用map函数转为ROW类型的RDD (RDD[Row])。
b) Step 1 被创建后,创建 Schema 表示一个 StructType(StructField(name,StringType,true),...) 匹配 RDD 中的 Rows(行)的结构。
c) 通过 SparkSession 提供的 createDataFrame 方法应用 Schema 到 RDD 的 RowS(行)。

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601


5、Spark通用的load和save操作、Save Mode保存模式

对于Spark SQL的DataFrame来说,无论从什么数据源(json、csv、parquet等格式对的文件)创建出来的DataFrame,都有一些共同的load和save操作。load操作主要用于加载数据,创建出DataFrame;save操作,主要将DataFrame中的数据保存到文件中。

Java版本

DataFrame df=sqlContext.read().load("user.parquet")

df.select("name","age").write().save("result.parquet")

scala版本

val df=sqlContext.read.load("user.parquet")

sf.select("name","age").write.save("result.parquet")

手动指定数据源类型:(比如将json格式的文件保存到parquet文件中)

Java版本

DataFrame df=sqlContext.read().format("json").load("people.json")

df.select("name","age").write().format("parquet").save("result.parquet")

scala版本

val df=sqlContext.read.format("json").load("user.json")

df.select("name","age").write.format("parquet").save("result.parquet")

Save Mode 的四种保存模式 

1)Save 操作可以使用 SaveMode,可以指定如何处理已经存在的数据。

2)Save Mode 的四种保存模式 SaveMode.ErrorIfExists、SaveMode.Append、SaveMode.Overwrite、SaveMode.Ignore

如:jsonDF.write.mode(SaveMode.ErrorIfExists).save("data/sparksql/people.parquet")

6、parquet文件

a) 编程的方式加载parquet文件

编程的方式加载parquet文件创建DataFrame;然后将其注册到临时表然后使用SQL查询需要的数据;对查询出来的DataFrame进行transform操作,处理数据。

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

b) parquet数据源自动分区的推断

表分区是一种常见的优化方式,比如Hive中就提供了表分区的特性。在一个分区表中不同分区的数据通常存储在不同的目录中,分区列的值通常就包含在分区目录的目录名中。在SparkSQL中的parquet数据源,支持自动根据目录名推断出分区的信息。

如:我们将人口数据存储在分区表中,并且使用性别和国家作为分区表。那么目录结构可能如下表示:

path
└── to
    └── table
        ├── gender=male
        │   ├── country=US
        │   │   └── data.parquet
        │   ├── country=CN
        │   │   └── data.parquet
        └── gender=female
            ├── country=US
            │   └── data.parquet
            ├── country=CN
            │   └── data.parquet
            └── ...

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

c) 合并schema (元数据)

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

7、Json文件

加载json文件为DataFrame、将其注册为临时表,使用sql进行数据的转换操作。

8、Hive数据源实战

在实际项目开发中,SparkSQL是从hive中加载数据,并进行数据的转换操作的。

  

1、第一个功能:使用hiveContext 的sql或者hql方法;可以执行hive中能够执行的hqlQL语句。
2、 第二个功能 执行sql返回一个DataFrame 用于查询。
3、第三个功能 使用saveAsTable()方法 将DataFrame中的数据保存到hive表中。
4、第四个功能  可以使用table() 方法 针对hive表 直接创建DataFrame。

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

9、JDBC数据源实战

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

10、SparkSQL内置函数的使用

 

1)聚合函数

案例实战:根据用户每天的访问和购买日志,统计每天的uv和销售额。

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

2) 开窗函数(窗口函数)

spark 1.4x 之后,为SparkSQL和DataFrame引入了开窗函数,比如最经典、最常用的 row_number() 可以让我们实现分组取topN的逻辑。

案例:统计每个种类的销售额排名前3的产品。

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

11、SparkSQL UDF 自定义函数的使用

 创建一个自定义函数,用来统计字符串的长度。

1)定义和注册自定义函数

a) 定义函数: 自己编写匿名函数。

b) 注册函数: SQLContext.udf.register(param1,func)  其中 param1是函数的名字,func是匿名函数用来处理业务逻辑。

2)使用自定义函数(在自己的sql中使用)

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

12、SparkSQL UDAF 自定义聚合函数的使用

UDF自定义函数,其实更多的是针对单行输入,返回一个输出。

而 UDAF是针对多行输入,进行聚合计算后,返回一个输出,功能更加强大。

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

13、SparkSQL 性能优化

14、核心源码深度剖析

 

15、案例实战 

SparkSQL与SparkCore结合 统计每日top3热点搜索词

数据格式:

日期    用户    搜索词    城市    平台    版本

需求:

1)筛选出符合查询条件的数据

2)统计出每天搜索uv排名前3的搜索词

3)将数据保存到hive表

详细代码链接:https://blog.csdn.net/yangshaojun1992/article/details/88996601

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值