本文为Spark学习总结
Apache Spark是用于大数据处理的集群计算框架,它并未使用MapReduce作为执行引擎,而是使用自己的分布式运行环境在集群上执行工作。Spark最突出的表现在于它能将作业与作业之间产生的大规模数据集存储在内存中
Spark还是用于构建分析工具的出色平台。为此,Spark项目包括机器学习(MLlib)、图算法(GraphX)、流计算(Spark Streaming)和SQL查询(Spark SQL)等模块。这些模块本文暂不讨论
弹性分布式数据集(RDD)是Spark最核心的概念,它是在集群中跨多个机器分区存储的一个只读的对象集合
在典型的Spark程序中,首先要加载一个或多个RDD,它们作为输入通过一系列转换得到一组目标RDD,然后对这些目标RDD执行一个动作,例如计算出结果或者写入持久存储器
“弹性”指的是Spark可以通过重新安排计算来自动重建丢失的分区
1)创建
RDD的创建有三种方法:来自一个内存中的对象集合(也称并行化一个集合);使用外部存储器(例如HDFS)中的数据集;对现有的RDD进行转换
2)转换和动作
3)持久化
4)序列化
默认情况下,Spark在通过网络将数据从一个executer发送到另一个executer时,或者以序列化的形式缓存数据时,所使用的都是Java序列化机制。Java序列化机制为程序员所熟知,但从性能和大小来看,这种做法效率并不高。使用Kryo序列化机制对于大多数Spark程序都是一个更好的选择。Kryo是一个高效通用的Java序列化库
1)广播变量
广播变量在经过序列化后被发送给各个executer,然后缓存在那里,以便后期任务可以在需要时访问它。它与常规变量不同,常规变量是作为闭包函数的一部分被序列化的,因此它们在每个任务中都要通过网络被传输一次。广播变量的作用类似于MapReduce中的分布式缓存,两者的不同之处在于Spark将数据保存在内存中,只有在内存耗尽才会溢出到磁盘上
2)累加器
广播变量是单向传播的,即从driver到任务,因此一个广播变量是没有办法更新的,也不可能将更新传回driver。要想做到这一点,我们需要累加器。累加器是在任务中只能对它做加法的共享变量,类似于MapReduce中的计数器。当作业完成后,driver程序可以检测累加器的最终值
Spark作业有两个独立的实体:driver和executer。driver负责托管应用(SparkContext)并为作业调度任务。executer专属于应用,它在应用运行期间运行,并执行该应用的任务。通常,driver作为一个不由集群管理器(cluster manager)管理的客户端来运行,而executer运行在集群的计算机上
1)作业提交
当对RDD执行一个动作时,会自动提交一个Spark作业。从内部看,它导致对SparkContext调用runJob(),然后将调用传递给作为driver的一部分运行的调度程序。调度程序由两部分组成:DAG调度程序和任务调度程序。DAG调度程序把作业分解为若干阶段,并由这些阶段构成一个DAG。任务调度程序则负责把每个阶段的任务提交到集群
2)DAG的构建
要想了解一个作业如何被划分为阶段,首先需要了解在阶段中运行的任务的类型。有两种类型的任务:shuffle map任务和result任务。从任务类型的名称可以看出Spark会怎样处理任务的输出
shuffle map任务
shuffle map任务就像是MapReduce中shuffle的map端部分,每个shuffle map任务在一个RDD分区上运行计算,并根据分区函数把输出写入一组新的分区中,以允许在后面的阶段中取用(后面的阶段可能由shuffle map任务组成,也可能由result任务组成)。shuffle map任务运行在除最终阶段之外的其他所有阶段中
result任务
result任务运行在最终阶段,并将结果返回给用户程序(例如count())。每个result任务在它自己的RDD分区上运行计算,然后把结果发送回driver,再由driver将每个分区的计算结果汇集成最终结果。最简单的Spark作业不需要使用shuffle,因此它只有一个由result任务就构成阶段,这就像是MapReduce中的仅有的map作业一样。比较复杂的作业要涉及到分组操作,并且要求一个或多个shuffle阶段
3)任务调度
当任务集合被发送到任务调度程序后,任务调度程序为该应用运行的executer的列表,在斟酌位置偏好的同时构建任务到executer的映射。接着,任务调度程序将任务分配给具有可用内核的executer(如果同一应用中的另一个作业在运行,则有可能分配不完整),并且在executer完成任务运行时继续分配更多的任务,直到任务集合全部完成。默认情况下,每个任务分配到一个内核,不过也可以通过设置spark.task.cpus来更改
请注意,任务调度程序在为某个executer分配任务时,首先分配的是进程本地(process-local)任务,再分配节点本地(node-local)任务,然后分配机架本地(rack-local)任务,最后分配任意(非本地)任务或者推测任务,如果没有其他任务候选者的话。这些被分配的任务通过调度程序后端启动。调度程序后端向executer后端发送远程启动的消息,以告知executer开始运行任务
当任务完成或失败时,executer都会向driver发送状态更新消息。如果失败了,任务调度程序将在另一个executer上重新提交任务。若是启动了推测任务(默认不启用),它还会为运行缓慢的任务启动推测任务
4)任务执行
executer以如下方式运行任务。首先它确保任务JAR的包和文件依赖关系都是最新的。executer在本地高速缓存中保留了先前任务已使用的所有依赖,因此只有在它们更新的情况下才会重新下载。第二步,由于任务代码是以启动任务消息的一部分而发送的序列化字节,因此需要反序列化任务代码(包括用户自己的函数)。第三步,执行任务代码。请注意,因为任务运行在与executer相同的JVM中,因此任务的启动没有进程开销
任务可以向driver返回执行结果。这些执行结果被序列化并发送到executer后端,然后以状态更新消息的形式返回driver。shuffle map任务返回的是一些可以让下一个阶段检索其输出分区的信息,而result任务则返回其运行的分区的结果值,driver将这些结果值收集起来,并把最终结果返回给用户的程序
负责管理executer生命周期的是集群管理器(cluster manager),Spark提供了好多种具有不同特性的集群管理器
1)本地模式
本地模式时,有一个executer与driver运行在同一个JVM中。这种模式对测试或运行小规模作业非常有用。这种模式的主URL为local(使用一个线程)、local[n](n个线程)或local(*)(机器的每个内核一个线程)
2)独立模式
独立模式的集群管理器是一个简单的分布式实现,它运行了一个master以及一个或多个worker。当Spark应用启动时,master要求worker代表应用生成多个executer进程。这种模式的主URL为spark://host:port
3)Mesos模式
类似YARN模式,略
4)YARN模式
YARN是Hadoop中使用的资源管理器。每个运行的Spark应用对应一个YARN应用实例,每个executer在自己的YARN容器中运行。这种模式的主URL为yarn-client或yarn-cluster。YARN是唯一一个能够与Hadoop的Kerberos安全机制集成的集群管理器
为了在YARN上运行,Spark提供了两种部署模式:YARN客户端模式和YARN集群模式。YARN客户端模式的driver在客户端运行,而YARN集群模式的driver在YARN的application master集群上运行
1)YARN客户端模式
对于具有任何交互式组件的程序(例如spark-shell)都必须使用YARN客户端模式。客户端模式在构建Spark程序时也很有用,因为任何调试输出都是立即可见的
在YARN客户端模式下,当driver构建新的SparkContext实例时就启动了与YARN之间的交互。该Context向YARN资源管理器提交一个YARN应用,YARN资源管理器则启动集群节点管理器上的YARN容器,并在其中运行一个名为SparkExecutorLauncher的application master。ExecutorLauncher的工作是启动YARN容器中executer,为了做到这一点,ExecutorLauncher要向资源管理器请求资源,然后启动ExecuterBackend进程作为分配给它的容器
每个executer在启动时都会连接回SparkContext并注册自身。这就向SparkContext提供了关于可用于运行任务的executer的数量及其位置的信息,这些信息被用在任务的位置偏好策略中。启动的executer的数量在spark-shell、spark-submit或py-spark中设置(如果未设置,则默认为两个),同时还要设置每个executer使用的内核数(默认值为1)以及内存量(默认值为1024MB)。YARN资源管理器的地址并没有在主URL中指定,而是从HADOOP_CONF_DIR环境变量指定的目录中的Hadoop配置中选取
2)YARN集群模式
YARN集群模式适用于生成作业(Production job),因为整个应用在集群上运行,这样做更易于保留日志文件(包括来自driver的日志文件)以供稍后检查。如果application master出现故障,YARN还可以尝试重新运行该应用
在YARN集群模式下,用户的driver程序在YARN的application master进程中运行。使用spark-submit命令时需要输入yarn-cluster的主URL,所有其他的参数,比如–num-executors和应用JAR,都与YARN客户端模式相同。spark-submit客户端将会启动YARN应用,但是它不会运行任何用户代码。剩余的过程与客户端模式相同,除了application master在为executer分配资源之前先启动driver程序外