Spark学习烦躁之旅

1.什么是Spark

spark是一个集群运算框架,因为使用了基于内存的运算和DAG优化使得其运算速度大大的加快。spark相当于是hadoop中map reduce的一个升级版,在很多方面的性能都优于map reduce。所谓的基于内存的运算指的是spark将每一次map reduce计算的结果存储于内存中,下一次map reduce可直接从内存中读取数据,这加速了需要多次迭代运算的情况。使用spark需要搭配集群管理员和分布式文件存储系统。

2.Spark运行原理

2.1 Yarn集群部署说明

首先我们来看一下目前最流行的yarn集群管理器,说明其运行的原理。spark还有自带的stand alone集群管理器,比较简单,但是应用范围没有yarn广。
主要对象介绍:
ResourceManager(RM):负责对各个NodeManager(NM)上的资源进行统一的管理和调度,主要分为两部分ApplicationsManager,管理所有的应用程序;Resource Scheduler 将资源分配给各个应用程序。
NodeManager:每个节点上的资源和任务管理器,定时的向ResouceManager汇报本节点的资源使用情况和各个Container的运行情况。负责执行AM的Container启动停止等请求。
ApplicationMaster:管理单个应用程序,向RM申请资源,监控任务运行的状态,并在任务运行失败时重新申请资源启动任务
Container:是Yarn对资源的抽象表示,封装了某个节点的多维度资源,如内存,cpu,IO等。
下面简述一下yarn的整体流程:
1.Client向ResourceManager提交应用程序
2.ResourceManager启动一个Container用于ApplicationMaster
3.ApplicationMaster启动,并向ResourceManager注册自己,保持心跳
4.ApplicationMaster向ResourceManager申请相应数量的Container资源用于处理任务
5.ResourceManager返回给ApplicationMaster申请的Container资源信息,ApplicationMaster对Container进行初始化,并且与NodeManager进行通信,要求NodeManager启动Container。并且与NodeManager保持心跳,监控和管理运行在NodeManager上的任务。
6.Container在运行期间,ApplicationMaster对Container进行监控,Container通过RPC协议向ApplicationMaster汇报任务的进度状态的信息,
7.应用运行的期间,client直接与ApplicationMaster进行通信,获得应用的进度、状态等信息。
8.应用运行结束后,ApplicationMaster向ResourceManager注销自己,属于ApplicationMaster的Container资源被回收。
通俗来讲就是:ResourceManager是老板,管理所有的资源。他手下有两个副总ApplicationsManager(管理项目)和ResourceSchedule(管理人事资源),负责处理具体的事务。当有用户项目来的时候,ApplicationsManager副总先找些人事成立一个专项工作小组ApplicationMaster,该项目的所有事宜都全权交给ApplicationMaster。并且对这个小组的工作情况进行监控,保证项目进度。ApplicationMaster分析了一下需求,向ResourceSchedule副总申请资源,告诉他我需要多少人来干活。ResourceSchedule分析之后,觉得nodemanager资源部的Container这些人比较合适,就分给了ApplicationMaster。Container就直接归ApplicationMaster管理,并且向其汇报工作。在项目进行的过程中,客户Client直接和专项小组ApplicationMaster对接,了解进度。等到任务接受,客户和公司的合作终止。专项小组ApplicationMaster被解散,其原来申请的资源Container就不停他们的了。
在这里插入图片描述

2.2 对比spark standalone运行原理

上面聊了Yarn运行原理,再次来看spark standalone就觉得整个世界都清净了。spark standalone中Master负责管理所有的集群资源,Worker就是每个节点资源,负责处理任务。其流程为Client提交任务,通过Driver调度资源。Driver向Master申请资源,Master分配Worker资源给Driver(因为服务器启动后Master和Worker保持着心跳,所以可以了解每个Worker的资源情况)。Driver和Worker直接通信,把任务交给Worker执行,Worker向Driver汇报任务的执行情况。任务运行结束以后,Driver向Master申请注销资源,相关的Worker会被释放。
其中根据使用的部署模式不同Driver可能在本地启动(client模式),也可能在某一个集群节点上启动(cluster模式)。
对比一下spark standalone和Yarn,其实两者都是主从的集群模式。一个主负责管理所有的资源,并且根据需求对资源进行分配。从节点执行具体的任务,并且直接和应用对接。区别在于一些具体的实现和功能划分不同而已。
在这里插入图片描述

2.3 Spark原理说明

  • Driver

    spark作业提交的时候,会创建一个Driver进程。Driver进程中包含SparkContext对象,SparkContext在初始化的时候会构建DAGSchedule和TASKSchedule对象。其中DAGSchedule负责对提交的每个job任务切分,TASKSchedule将任务发送到Master,进行application的注册以及资源的申请。
  • 资源申请

    Driver进程向资源管理器(standalone、yarn)申请资源。资源管理器会根据申请参数,在每个节点上启动一定数量的executor进程。每个executor进程对应一定数量的内存和cpu-core;
  • 执行任务

    Driver中的DAGSchedule会将我们的job分为多个stage(spark会将shuffle操作作为stage的界限,即shuffle之前是一个stage,shuffle之后是下一个stage),每一个stage中又会分为多个task(一个pipeline作为一个task),task是最小的执行单元。TASKSchedule将task发送到executor进行计算。当一个stage中的所有的task都执行完了以后,会将中间结果写入到磁盘中。然后driver开始调度执行下一个stage,下一个stage的输入数据就是上一个stage存储的中间结果。不断的迭代直到所有的stage都执行完,得到我们最终想要的结果。
  • executor说明

executor中包含等多个cpucore资源,一个cpucore可执行一个线程,executor可以将cpucore数量个task任务并行执行。 如果对RDD执行了cache或者persist等持久化操作,根据持久化策略的不同,executor会将计算出的数据写入到executor所在的内存或者磁盘中;所以executor分为三个部分:第一部分负责执行我们的代码,默认占executor的20%内存;第二部分负责从上一个stage拉取数据,默认占executor的20%内存;第三块负责持久化RDD,默认占executor的60%内存。task任务执行的速度和executor的内存以及对应cpu-core数量有很大的关系,因为每一个task需要一个线程来执行,每一个cpu-core对应的是一个线程。在task的数量和cpu-core的数量分配合理的情况下,可以保证尽可能多的task同时计算,并且不会产生等待,这样速度就会很快。

总结一下就是,每个spark作业会有一个Driver进程。Driver向集群管理器申请资源,集群管理器会返回适量的Executor进行执行具体的任务。Driver按照shuffle将作业划分为多个stage,每个stage按照pipeline生成多个task任务。将这些task交给Executor进行处理,Executor中有多个cpu-core,每个都对应一个线程,可执行一个task。一个stage执行完成之后,会将数据写到磁盘,供下一个stage使用。

3.hadoop map reduce shuffle说明

对于hadoop来说,shuffle分为两个阶段map和reduce,其中map阶段负责准备数据,即将数据分别处理之后写入到磁盘中。reduce阶段使用数据,即拉取map处理之后的数据,进行reduce处理,并写入到磁盘中。我们简单描述一下下图的流程,
map:
首先从HDSF(这只是个例子)文件系统中读取到数据,并且进行split切分。对每一块切分后的数据执行map相关操作,map处理后的数据会写入到缓存区。当缓存区满的时候进行spill溢写操作,将缓存区的数据写入到磁盘临时文件中,这个过程我们可以看到三个操作,partition:分区,根hash(key)/num_reducer取模 计算key所对应的分区;sort:按照key进行排序,这样相同的key可以放到一起,方便快速的处理统一个分区的数据;sipll就是溢写入临时文件中。
因为一个task会有多次溢写的操作,生成多个小的临时文件。使用merge将多个小文件进行合并为大文件。
在这里插入图片描述
使用这张图可以看清楚reduce的操作,不同的reduce从多个node节点的磁盘中去copy自己对应分区的数据。然后进行排序。排序完成之后进行reduce操作,最后输出,将其写入到HDFS文件系统中。其中shuffle主要是map和reduce中间数据处理传输的部分,包括map写入到缓存和磁盘,reduce从磁盘中读取数据的过程。对于海量数据来说,这通常会是影响性能很关键的部分。
在这里插入图片描述

4.RDD(Resilient Distributed Dataset)

RDD(弹性分布式计算集合)是 spark的计算模型,是spark中最基本的数据抽象,也是spark的核心。它代表一个不可变、可分区、里面的元素可并行计算的集合。

4.1RDD属性

1.一组分区(partation),数据集的基本组成单位。
2.一个计算每个分区的函数
3.RDD之间的依赖,RDD每次转换会生成一个新的RDD
4.一个partationer,RDD的分片函数
5.一个列表,存储存取每个partation的优先位置。spark在 计算的时候,会尽可能的将计算任务分配到数据存储的位置。

4.2RDD简单流程演示

简单了解一下RDD的执行流程,下面hello.txt是一个存储在HDFS文件存储系统上的文件,分为3个block块。使用spark的textfile读取每个block块,并分区为3个partation,这是RDD1。接着使用flatmap算子将按‘,’切分后的数据压平,RDD1转换为了RDD2。接着使用map算子,将每个值变为值和数字映射的形式,RDD2转换为了RDD3。这块的map和平时意义上的map有点不同,因为不要求键必须唯一,我开始也是纠结了。最后使用reducebykey算子,将RDD3转换为RDD4,得到每个单词出现的次数。
在这里插入图片描述
在这里插入图片描述

4.3RDD持久化

因为transformation只是进行RDD之间的转换,并不进行真正的计算。所以一般情况下,针对一个RDD的action操作完成了之后,该RDD就会被销毁。那么如果下面还有用到该RDD,关于RDD的逻辑需要重新计算一次。这样的话就眼中影响了程序性能。
所以对于一些会重用的RDD,我们会考虑将其结果进行缓存,再次使用的时候只需从缓存中读取即可。
持久化有两种方式cache和persist。cache就是无参的persist,相当于直接缓存到内存中。persist支持多种缓存的策略,一般默认的是MEMORY_ONLY。
在这里插入图片描述

4.4RDD的宽依赖和窄依赖

如图所示,一个RDD转换为另外一个RDD时。如果父RDD中的一个partation只指向子RDD中的一个partation,则成为窄依赖。否则称为宽依赖。
在这里插入图片描述

4.5依赖关系下的动态流程图

spark对于stage的划分是根据宽依赖来进行的,从左往右,每遇到一个宽依赖就将其切断,之前的划分为同一个stage。因为partation的依赖性,窄依赖就被划分到同一个task中,可以在同一个线程中处理,也尽量放在同一个物理机器上(这样可以更快),而宽依赖就只能等父节点处理完成后再处理。然后,每个stage中的task数量,是由最后一个RDD的partation数量决定的。这就是spark对于 DAG的优化了,我们提到的spark速度快的另外一个方面。
在这里插入图片描述

5.RDD的算子

RDD主要支持两种类型的算子transformation和action。transformation主要是将一个RDD转换为另外一个RDD,具有lazy特性,也就是说transformation的算子并不会真正的执行。action执行真正的计算操作,这种设计可以提升spark的效率。
在这里插入图片描述
整理了一些遇到的算子,这种东西还是用到的时候再整理比较好,具体有多少算子能出现在这里,就看我以后跟spark的缘分了

  • transformation-value类型算子
    map:将原来RDD中的每个元素通过f函数映射为一个新的元素
    flatMap:将原来集合中的每个元素映射为一个新的值,并且将所有的集合压缩为一个集合
    groupBy:按照对value的某种处理得到key,然后按照key进行分组
    filter:通过某种规则对元素进行过滤
    distinct:返回一个新的RDD,值为原来的RDD去重后的值。
    zipWithIndex:按照元素的顺序对元素进行压缩
    randomSplit:随机的将一个RDD切分为多个RDD,按照给定的weights分配数据
    persist/cache:对RDD进行持久化,persist根据指定的参数选择存到内存中或磁盘上
  • transformation-mapvalue类型算子
    reduceByKey:对key相同的元素进行reduce操作
    groupByKey:按照key的值进行分组
    sortByKey:按照key值排序
    collectAsMap:将成对的元素作为键值对返回,相当于字典
  • action类型算子
    foreach:对每个元素应用函数
    collect:返回一个列表,包含rdd中所有的元素
    count:返回RDD中的元素个数

6.python开发配置

  1. 将下载好的spark中的python/lib 目录下(全部两个zip文件)的pyspark.zip和py4j-0.10.4-src.zip配置在对应的项目下
    在这里插入图片描述
    2.在运行时变量中配置SPARK_HOME为spark所在的路径
    3.报错java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries
    需要在运行时变量中配置hadoop所在的目录,只是为了消除报错,其实并不会对程序有实际的影响。

7.总结

本文中我们介绍了常用的大规模数据处理框架Spark,Spark主要负责处理数据的处理部分。相当于Map-reduce。虽然也有自己的集群管理架构,但我们实际中一般会使用Yarn来进行集群部署。并且使用HDFS作为文件分布式文件存储系统。接着我们介绍了Spark运行的原理,主要就是使用Driver进行资源的申请,以及根据Shuffle进行stage的切分,按照pipeline进行Task优化。然后介绍了Spark中的数据抽象RDD,RDD相当于Partition的集合,支持多种算子的操作。总体上来说Spark是优于Hadoop中的Map-reduce,从运行速度,编码的简单等许多方面。本篇只是开始学习,总结一些相关的知识点。一些具体的细节,还有待后续补充。

8.挪威的森林

最近闲暇时间在看村上春树的《挪威的森林》。其实在很久之前的一个梦里,我非常真实的见到了村上春树,并且梦中的村上春树好像是我爸爸。前一阵子追了一个言情剧《半是蜜糖半是伤》,其中的男主家里有一本《挪威的森林》。我觉得跟村上先生的缘分还真是不浅,如果再不了解一下他就有点不好意思了。但是我了解他的第一步却是去百度上搜索“村上春树死了吗?”,真的非常抱歉!当然也很感激他还活着。先生说过“活到26岁,然后死去”,啊!好幸运我今年27岁了。回忆总有一种淡淡的忧伤的美,但是让人欣慰的是,我并没有感觉到自己失去了什么。最让我感激自己的就是小时候捡到的一块钱,我并没有将它据为己有,而是买了包辣条和朋友们分着吃(本来想说归还给失主来着,可惜我并没有那么做,我可不是个欺世盗名的卑鄙小人)。可是与回忆不同的是想象,对于本可以拥有的美好事物的想象总是让人心痛的。那些听着伤感的音乐,默默流泪的人总是让我很羡慕。因为失去就表示了曾经拥有过,曾经拥有过就表示还有回忆的资格。同样的,先生的森林对于其它人来说是一种淡淡的忧伤,对于我来说却只是别人的地狱,别人的天堂!

风铃公主

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值