Fork me on GitHub

Spark Core进阶

一、Spark核心概念

在Spark集群概念中有一些术语是需要我们理解的:

其中重要的概念是:

1、Application

基于Spark开发的应用程序程序,一个应用程序是由一个driver和一个或者多个executors组成。

2、Driver Program

应用程序中运行的主函数,它创建了一个SparkContext。

3、Cluster manager

  一个外部服务用于在集群上获取资源,比如之前我们通过spark-submit提交脚本,其中有一个参数是--master local[2]/spark://hadoop-master:7077,就是向这个local或者spark集群获取运行这个脚本任务的内存之类的资源。

4、Deploy mode

看driver运行在哪,如果是在集群里面就是cluster模式,如果driver运行在客户端就是client模式。

5、Worker node

运行应用程序的集群节点,这些Worker node就相当与Standalone模式中slaves文件中写的主机地址。

6、Executor

 Application运行在Worker node上的进程;处理tasks,并且将tasks的结果存储在内存中或者硬盘中,每一个Application有它自己的Executors。

7、Task

Executor执行的最小工作单元。

8、Job

一个action对应一个job,并行的取计算由job得到的一系列的tasks。

9、Stage

每一个job并分成一系列的tasks,被称为stages。

所以,总结一下就是

(1)一个应用程序是由一个driver和一个或者多个executors组成

(2)executors是运行在Worker node上面的

(3)Worker node上面有一系列的tasks

(4)tasks是由driver发送过来的

(5)tasks是job产生的

(6)当遇到action时,会触发产生job

二、Spark运行架构

  看上面的结构图有三大部分:Driver Program、Cluster Manager、Worker Node,每一个spark的应用程序在集群中运行是相互独立的,是由主程序(Driver Program)来进行协调的;当应用程序在集群上运行时,Driver Program中的SparkContext会去连接Cluster Manager(standalone cluster manager, Mesos or YARN),Cluster Manager主要就是为应用程序获取资源,诸如需要的内存之类的等;当连接后,Spark获取集群中Worker Node上的Executors,这些Executors是对你的应用程序进行运行、计算以及存储运行后的数据,那么它们运行的应用程序代码以及任务都是有SparkContext进行发送的。

需要注意的是:

  • 每一个应用程序之间都是相互独立的,有它自己的Executors,也就是应用程序之间的数据是不能直接共享的,除非你将一个应用程序执行的结果先存储起来,后面的应用程序才可以使用
  • Driver Program和Work node上是保持通信的状态,并且监听Executors的整个生命周期,这样也是时刻掌握Executors是否挂掉
  • driver运行时应该更应该接近Work node,如果是远程,对于driver打开RPC

三、Spark与Hadoop重要概念区分

Hadoop与Spark有以下特点和区别:

1、Hadoop特点

(1)一个MR程序就是一个Job

(2)一个Job是由一个或者多个Task(Map/Reduce)组成

(3)一个Task对应一个进程

(4)Task运行时开启进程,Task执行完毕后销毁进程,对应多个Task来说,开销进程比较大

2、Spark特点

(1)一个Application是由一个Drive(main方法中创建SparkContext)和一个或者多个Executor组成的

(2)一个Application是由一个或者多个Job组成

(3)一个Job就是一个Action

(4)一个Job是由一个或者多个Stage组成

(5)一个Stage是由一个或者Task组成

(6)一个Task对应一个线程,多个Task以并行的方式运行在一个Executor中

四、Spark Cache详解

1、Cache的使用

首先启动spark,进入/root/app/spark-2.0.2-bin-hadoop2.6/bin,执行:

[root@hadoop-master bin]# ./pyspark 

此时已经启动了spark,可以在web界面进行查看:

 

我们可以执行一个job

>>> lines= sc.textFile("file:///root/hadoopdata/wordcount.txt")
>>>lines.count()
2            

那么,如何对这样的一个结果进行缓存呢?Cache和Transformation是一样的都是lazy的,只有遇到Action才会执行

>>> rdd.cache()
file:///root/hadoopdata/wordcount.txt MapPartitionsRDD[4] at textFile at NativeMethodAccessorImpl.java:-2
>>> rdd.count() #此时有action操作,触发上面的cache操作
2         

下次再执行,就会直接从内存中取出数据:

>>> rdd.count() #速度很快

2、源码

 在cache方法中:

    def cache(self):
        """
        Persist this RDD with the default storage level (C{MEMORY_ONLY}).
        """
        self.is_cached = True
        self.persist(StorageLevel.MEMORY_ONLY)
        return self

调用的是persist方法进行持久化:

    def persist(self, storageLevel=StorageLevel.MEMORY_ONLY):
        """
        Set this RDD's storage level to persist its values across operations
        after the first time it is computed. This can only be used to assign
        a new storage level if the RDD does not have a storage level set yet.
        If no storage level is specified defaults to (C{MEMORY_ONLY}).

        >>> rdd = sc.parallelize(["b", "a", "c"])
        >>> rdd.persist().is_cached
        True
        """
        self.is_cached = True
        javaStorageLevel = self.ctx._getJavaStorageLevel(storageLevel)
        self._jrdd.persist(javaStorageLevel)
        return self

传入的参数是:StorageLevel.MEMORY_ONLY,如果删除cache那么就使用unpersist:

    def unpersist(self):
        """
        Mark the RDD as non-persistent, and remove all blocks for it from
        memory and disk.
        """
        self.is_cached = False
        self._jrdd.unpersist()
        return self

这个unpersist方法是立即执行的。

比如:想改变一下缓存的策略

>>>from pyspark import StorageLevel
>>>lines= sc.textFile("file:///root/hadoopdata/wordcount.txt")
>>>lines.persist(StorageLevel.MEMORY_ONLY_2)
>>>lines.count() #缓存执行

五、Spark Lineage详解

RDD具有血缘关系,RDD1是通过RDD0来的,如果RDD1中的partition1丢失,那么可以通过RDD0中的partition1找回,这是很好的容错机制。

六、Spark Dependency详解

 1、Narrow

Narrow依赖是一个父RDD的partition最多被子RDD的某个partition使用一次,这也就意味着如果子RDD的某个pattion丢失,可以较为容易的从对应的父RDD的partition获取。

可以看到上面的三种情况都是子RDD的partition最多使用一次父RDD中的某个partition。

2、Wide

一个父RDD的partition被子RDD的partition使用多次,那么怎么判断父RDD的partition被子RDD的partition使用多次?注意使用过程中如果有shuffle过程就会出现Wide依赖。

那么什么时候会出现shuffle呢?

一般像算子中带有“ByKey”的方法需要注意,比如:groupByKey、reduceByKey以及join操作(cogroup、join)。这时会造成shuffle过程。

图一和图二中都涉及到了shuffle过程,所以就是Wide依赖。

3、实例说明

以wordcount实例说明这个过程,在wordcount中使用到的算子有flapMap、map、reduceByKey,其中前两个都是Narrow依赖,reduceByKey是Wide依赖。

以hdfs文档中WordCount.txt文档为数据源,其内容为:

hello world
hello sandy
hello world
hello sandy
hello world
hello sandy

 上图中需要注意的是:

(1)reduceByKey会造成shuffle过程

(2)shuffle过程会将job分成Stage0和Stage1,也就是说凡是shuffle会将job分隔成不同的Stage(一个shuffle会有两个Stage,两个shuffle会有三个Stage)

(3)Stage中执行的就是不同的Task(比如Stage0中的flatMap和map的Task)

 

详情参考:http://spark.apache.org/docs/2.0.2/cluster-overview.html

 

posted @ 2020-04-18 22:39  iveBoy  阅读(138)  评论(0)    收藏  举报
TOP