spark理论笔记2.docx
- 文档编号:27210363
- 上传时间:2023-06-28
- 格式:DOCX
- 页数:23
- 大小:7.26MB
spark理论笔记2.docx
《spark理论笔记2.docx》由会员分享,可在线阅读,更多相关《spark理论笔记2.docx(23页珍藏版)》请在冰豆网上搜索。
spark理论笔记2
好了,我们来看一下这个spark,当然有一些概念前面已经抛给大家了,所以这个spark它也是分布式的并行计算框架,然后通常来说spark是下一代的mapreduce,扩展了mapreduce的数据处理流程,我们看一下mapreduce它自身有哪些问题呢?
(如下图1所示)
图1:
首先这个调度慢,启动map和reduce进程太耗时,因为它是多进程嘛,它必然是会有这么一个缺点,然后计算慢是因为中间做partition或者是内部数据会有一些落地,还有api抽象比较简单,就是说你想实现一个你想要去实现的那个功能,但是你想用mapreduce框架,你只能去用map和reduce这两个原语来去完成,你不可能用第三个原语或者是自己再造一个,这是不允许的,必须要完全按照它框架要求去写,所以相对来说灵活性还不够高并且我们通常一个任务不是说那么特别简单就让你一个mapreduce就搞得定的是吧?
通常一个任务会有多个mapreduce,然后来共同完成我的这个功能,所以在对于我们之前学的那个mapreduce的过程对这种作业流的描述相对来说会缺乏一点,所以我们只能通过外围的shell脚本然后把这块的缺点进行一个弥补对吧?
就像我们之前学那个mapreduce做join的那个代码还记得吗?
就是首先第一步先对数据做一个数据的预处理,然后才能通过最后一个mapreduce做一个所有的数据的收集并且做排序,然后再去做前后两行的一个默值工作对吧?
那你想把这个预处理和后面的真正做join这个环节你想通过一个mapreduce来写的话那是肯定搞不定的,所以必须把它抽象成多个mapreduce来完成,所以对于这种需要多个环节来去完成一个比较大的一个作业流描述的话会比较困难一点,但是它在spark里面的话那对于这种作业流描述会更灵活一些,所以这相当于对之前学mapreduce它有哪些缺点之后我们看下spark,当然这mapreduce里面的这些缺点都变成了spark的优点对吧,那我们逐步来看一下这个spark是怎么样来去那个完成作业的支持的。
我们来看一下这代码(如下图2所示)
图2:
这代码就是一个wordcount,一个map进程和一个reduce进程,这个大家估计都很熟悉了是吧?
那么就是通过非常简单的语句来完成这个功能。
那这个spark,还是分布式的并行计算框架,我们来具体的看一下(如下图3所示)
图3:
然后就是下一代的mapreduce,扩展了数据处理流程,这个我们刚刚也说过了对吧?
然后这个executor封装在你的container里面运行,然后container默认内存是一个G,但是这个默认内存是可以调整的,然后你这个executor是可以对具体的executor去设一个预分配的一个内存,如果你想提交的这个任务,当然你可以设置它一共有多少executor来完成对吧?
比如说你这个num-executor,就是你想用多少个进程来去支撑我这个任务,然后这个executor-memory是每一个进程它内部有多少个内存,所以你整个的任务对需要的内存就是说这executor-memory*num-executor两个相乘,这个很好理解是吧?
然后AM在spark里面叫做driver,AM向RM申请这些executor资源,当分配完这些资源之后,这个executor启动,然后开始由AM来向这些executor进行分配task,那么分配多少task并且分配到哪一个executor这个是由AM来决定的,也就是由你的driver来决定,所以相当于这个driver就是整个spark任务的一个调度这么一个功能,所以真正去任务的最细粒度的这个节点它都是在executor这个里面去运行线程来组成的,所以可以把这个executor就想象成它是一个线程池,这个线程池里面有很多个task,然后像右面这一侧(如下图4所示)
图4:
上面是AM,下面都是由AM来管理的这些executor,然后executor内部有很多个task,然后因为executor里面这些线程都是已经申请预分配好的,所以你再去执行任务的时候直接去线程池里面去捞这个线程去执行就可以了,所以就省去了进程频繁启动停止的开销,这样的话spark会更快。
然后spark主要解决什么问题呢?
(如下图5所示)
图5:
主要是最大化利用内存cache,那么为什么最大化利用这个内存呢?
那刚才我们也说过了是吧?
相当于是多个线程共享这么一个内存资源,所以整体来看你内存是大大的优化了,并且因为你优化了这个内存,所以让你的内存可以承载更多的资源,所以在spark里面的话,那它主要就是靠你内存来去完成数据的转移,那中间结果由于不像mapreduce那样必须中间要落盘的操作,那spark里面完全通过内存才去做一个传输,所以迭代起来是非常快的,那这块有一个比mapreduce快多少倍,这个只是做一个参考,不一定是准确的。
主要来说明我spark同样去运行一个任务,spark通常情况下会比mapreduce更快,就是把这个cache的方式能够通过之前我们sql语句的方式来表达的话,类似于这么一种模式(如下图6所示)
图6:
上图6中有两个sql语句,这两个sql语句它有一个共同的条件,就是对某一个字段大于50这么一个条件控制,并且这两个sql语句读的表都是来自一个table,所以条件一致,并且这个表又是同一张表,那完全可以把中间的这个数据进行一个抽象,就提前我建立一张Cachetable表,然后把这个满足某一个字段大于50这么一个条件控制的这个表里面的数据提前给它加到内存里面去,然后这个数据有了,那一旦我想从这个数据里面去读数据的时候,那我直接from直接从这个Cachetable表里面去读就可以了,那相当于这个Cachetable表数据就是直接从内存读取的,那么这样就会很大的加速执行这样的一个情况。
所以这种的方式的话,那你既然有一个公共内存,那我就可以供下游很多个不同的任务处理流程来公共使用对吧?
然后我们上次讲到了action算子,那么action算子是什么呢?
大家还记得吗?
就是类似于某一些阶段性的执行操作,你可以把它想象成一些函数,想象成一些行为都可以,那这些算子有哪些呢?
算子一共包含两类,一类是Transformations类和Actions类(如下图7所示)
图7:
那Transformations类里面都是一些个函数,这里面每一个函数它的功能是不一样的,那这Transformations类和Actions类两类到底有什么区别呢?
那执行Actions类的时候就相当于刚才我们举得那个例子,就是说我一旦保存的时候就相当于我把之前的一段临时数据就完整的固化起来了,就是说你但凡它执行一次Actions,那我就把前面的执行的内容就我就只能执行一遍,然后这个Transformations就相当于是我的一些规划,所以这再举一个更通俗易懂的例子,比如说在小孩玩这个玩具,比如搭这个乐高玩具,然后这个乐高玩具它有很多很多的小组件来完成对吧?
然后对于一个小孩来说,它想用这个乐高玩具去搭的时候,它也不知道大概去搭什么,所以通常来说厂家在这个产品介绍页里面它会有一张图纸,你可以按照那个图纸来去模拟完成,就有点相当于我们再去建一个大厦的时候,首先你先把这个建筑结构把这张图纸先画出来,然后你在上面才能去围绕这个图纸才能去进行一个实际的建筑操作,那在这个图纸里面相当于是一个Transformations,就是它只有图纸,但实际中还不存在,它只有一个逻辑关系,但是实际它这个任务还没有开始执行。
那么Actions类就是图纸有了或者是我阶段性的图纸有了,我来了一次Actions就相当于是把我阶段性的图纸就变成现实。
这个理解吗?
图8:
再比如说我开发代码的时候(如上图8所示),那我代码写的过程中肯定是我去通过一些不管用scala语言也好,用python语言也好,都想表达出我想执行这个任务的一些逻辑对不对?
那相当于我这个逻辑就通过一些Transformations来去组成的,就是说假如说这个任务之后真正被运行了,那你就应该按照我规定的这个顺序来去执行,当然这个顺序就相当于是一个图纸,这个图纸还不是现实对吧?
好了一旦后面程序执行的时候,一旦运行到了一个Actions,那我这个Actions前面这一部分每一个环节都开始执行,那如果后面没有Action的话那相当于就是这个图纸白画了,就完全成为图纸,它永远成不了现实了,所以对于一个spark任务来说,那它肯定是要有Actions的。
那么这个Actions有什么呢?
如下图9所示
图9:
比如第一个是count(),这个count()就是计数,假设说我这个Actions是count(),那我前面都是一些数据处理流程,那我前面比如说第一个是input数据(如下图10所示)
图10:
我这个数据输入进来到两个T的环节,到了第二个T阶段的时候,那么这个阶段里面的数据一共有多少条记录,然后我通过这个Actionscount()就可以把它给计算出来了,那么collect()相当于是把这里面的数据都打印输出出来,这个当然到后面实践会给大家挑一些来给大家练习一下,那么第三个reduce就有点像mapreduce的那个聚合,然后lookup就是比如只把前100条数据读出来,所以lookup就是指前多少条数据的意思,然后save很重要,就是对于spark来说或者是不管mapreduce也好,你最终的目的是你得到了这个数据,然后并且把这个数据进行输出对吧?
那输出就是通过这个save的方式输出到这里面具体你指定的一个路径上面去。
我们来看一下Transformations(如下图11所示)
图11:
Transformations中的map是最常用的,map就相当于是一个一到一的关系,就是说你map前面有输入后面有输出对吧?
比如你输入10条数据,那么输出就10条数据。
那么filter就是过滤,就是把那些不满条件的记录过滤掉,然后flatMap就是一个一到多的关系,这个flatMap特别使用场景是比如求wordCount例子对吧?
比如你来一个句子,这个句子你经过分词然后可以分成很多个单词,那你来了一个句子我可以分出比如十个单词,二十个单词,那我输入是一条句子,输出是二十个word的,这就是一个一到多的关系,所以对于有这样的需求的情况下,那就需要flatMap,相当于把你这个数据进行打拚的一个操作,这个后面会有实例的,这个大家先不要着急,先提前有这么一个认识。
然后这个groupByKey就是对某一个key进行聚合,有点像做join似的,好了,那么既然是join那这块就相当于是shuffle做一次partition对不对?
所以这个groupByKey它是一个shuffle,reduceByKey也是一样。
那么union就相当于是把两个记录进行直接合并就可以了,然后join就是一个求交集,那么基本上工作中主要的就是以上我说的这几个算子会更常用一点。
好了,那既然了解完算子之后,那我们刚才不是说过如果你想实现一个wordCount,你想用mapreduce去写,那你需要去写一个map代码一个reduce代码,那我们想把这个wordCount的这个任务通过spark的方式来表达出来,那应该是怎么做呢?
这下面有一个案例(如下图12所示)
图12:
很简单,就相当于三行代码就搞定了,第一行代码是代表你的数据输入,最后一行代码是代表你的数据的输出,中间的代码是代表你的数据的处理,输入你是可以从本地输入也可以,那从远程hdfs上输入也可以,然后输入完之后它会有一个变量RDD来去承载,这个RDD等下我们会细说,那么这个图12第一行中的file就是代表着你的数据的输入对吧?
那接下来我后续的所有操作都是依赖于你的数据的输入来展开的对吧?
首先这个file输入然后这个点”.”后面再接应一个算子,就是说我刚刚讲的那些算子都是通过这种点”.”括号和大括号,相当于是一个接龙游戏一样,就相当于全贯穿下来,所以它开发起来是非常快的,用很少的代码就实现一个很复杂的功能。
那么我们读一下这个代码,大概去了解一下这个spark代码去怎么开发的,首先这个file它来自于外边这个HDFS上的数据,那这个数据比如说我们做一个wordCount,那这个数据肯定是有多条多行是把?
每一行代表一句话,然后每一句话里面都是拿空格分隔的一个单词一个单词对吧?
然后我对这里面这个数据的输入我开始去做split,把数据的输入每一条每一行记录每一句话去做一个split,按什么split呢?
就按照空格去做split,所以这个line.split这个输出什么呢?
就像python那样,我split完了之后它就是一个数组对不对?
那相当于行是一行,然后输出就是多个单词,然后flatmap会把这个多个单词给它打拚成多条记录,接下来每一条记录就开始去读word,然后word对于它见过的每一个单词它上面就追一个1,然后接下来在它这个spark里面默认前面word这一块是key,后面1是value,所以这个时候reduceByKey的时候会对这个key把后面这个1这个值进行聚合进行加法操作,然后最后得到一些结果,然后把这个结果输出到HDFS上,是这么一个过程。
接下来我们来看一下spark核心,在spark核心里面会去介绍相对更多的一些概念。
好了首先第一个很重要的概念就是我们刚才一直在去提到的RDD(如下图13所示)
图13:
那什么是RDD呢?
它是一个基于弹性分布式数据集的模型,就是说这个RDD的前后之间的组合就完成Spark的任务。
然后它有良好的通用性,容错性和并行处理能力,那这块有一个很重要的特点就是弹性分布式,什么是弹性呢?
那首先说这个内存,它内存是一个分布式内存,就是说它使用的时候不像这个mapreduce一样来回拷贝文件这样的使用,那所以这个分布式内存是弹性的就是说我整个集群来说我的内存是有限的,你再大的资源再大的内存你也会有放不下数据的时候,这个时候有点像我们mapreduce里面那个map阶段一样当我这个数据一旦达到了某一个水位线的时候,比如达到了百分之80,那我这个数据因为在内存里面存不下了,那我这个数据就往磁盘上进行一些个溢出,通过这种方式来去满足我内存存不下数据的这么一个情况,所以弹性主要是来集中在这一块,所以也不是你spark任务就完全不用磁盘,它也是用磁盘,除非它这个数据在你内存里面实在是存不下了或者是达到了一定程度了,它才会把数据临时性的往你磁盘上去做一个交换,但是一旦出现这种情况,那你spark任务的性能就会受到一些影响。
然后RDD这一块它是一个具有分区信息的数据集,具有懒惰性,什么是懒惰性呢?
就是刚才我们说的这个过程(如下图14所示)
图14:
我这中间过程中有很多的一些Transformations,然后一旦出现了shuffle的过程,它是一个两个独立的一个分水线,那所以对于一个你想让spark任务能够快速的执行,那最有效的方法就是尽量避免不用带有shuffle功能的算子,尽量使用那些一对一的算子,如果实在是避免不了了,那看看有没有其他的方式能够代替它,如果实在是代替不了的话那没办法,那只能去用这些带有shuffle功能的算子,那所以对于这种RDD来说那尽可能的是让这个数据尽可能的让你这个互相的这个依赖,这样的一个对于整个的任务的一个流程的一个表达,能够尽可能的往后拖延(如下图15)
图15:
就是说我这里面有Actions算子也有Transformations的算子,然后我这个Transformations前面会经历很多个Transformations这个过程,然后最后会去跟一个Actions算子,那这个Actions算子我是尽可能的往后拖,然后这个任务先有一个大概的一个表达,比如下图16
图16:
就是说我在这个图16这里面可以把我整个的spark任务能够通过这几个RDD能够描述起来,然后我每一个RDD它尽可能的不马上去执行我自己的任务,就是只要我能够不执行就不执行,那如果实在是遇到那种宽依赖的时候,实在是避免不了了那我才能去执行。
然后这个RDD很容易和我们之前学的那些比如你学python开发也好,学开发java也好和那些变量相混淆,那刚才我们在看这个代码的时候就以为这个conuts是一个变量,其实它是一个RDD(如下图17所示)
图17:
然后通过这个普通语言它这个背后是存有一块内存区域的对不对,那它是读这个变量里面是可以读到一些数据出来的,但是你这个RDD呢它是不保存你的数据的,RDD不包含待处理数据,真正的数据是只有在执行的时候才加载进来,那这个数据加载主要是来源于两个来源,一个是spark外部和一个是spark内部,那spark外部其实很好理解,它是从外面存储来的,比如从hdfs上来,那么内部是从其他RDD上来,那我们上面图17这个代码就是这样,我第一句话就是从外部的HDFS上来的,那我这个file就是一个RDD,那这个RDD本身是不代表你这个背后真实的数据,它只是告诉你后面的下游我这条水位线是通过这个RDD来帮你去做一个连接,然后并且这个数据它会有一些个partition的一些个概念,然后这个file这个RDD通过了很多算子的串联,然后最后得到了一个conuts,conuts也是一个RDD,所以在spark里面通常的数据来源要么就是从外部的数据源来,要么你就是从其他的RDD上来,所以有很多的处理环节都是说由一个RDD通过很多算子的变换,变换得到另外一个RDD的过程。
然后RDD,刚才我们讲过一个小孩玩积木的例子对吧?
那这里面为了让这个RDD能够更好的了解它是一个什么概念,那我们再借助这个例子给大家讲一下,比如小孩玩的这个积木,这个积木有很多种形状,有这种圆形的,有正方形的,还有圆柱形的等等,然后每一种形状都对应着一个partition,这有点类似于我们相同的key落到同一个桶里面是一个类似的概念对吧?
就每一个形状都对应着一个partition,然后每一个不同积木形状的积木都会给一个小朋友去玩,就是每一个小朋友他会拿到很多形状的积木去玩,那相当于这个小朋友就是类似于处理这些数据的一个task,那现在有一个老师比如想要这个小朋友来去完成一个任务,就想让这个小朋友将自己的这个积木去围成一个圈,所以这个时候大家都在自己玩自己的,而且是不允许这个小孩之间互相交换,就是不允许你这个数据之间是有shuffle的,但是当要求小朋友如果必须要用同一个形状积木来去搭建这么一个圆圈或者是搭建这么一个组合的时候,那么这个时候没有办法,那只能向朋友之间进行数据的交换对吧?
那这个时候它就产生了一些数据交互,这个过程叫做一个shuffle的过程,然后你交换还不是简简单单的交换,就是说你两个朋友之间的互相积木的交换,就是说你从这个圆到另外一个圆这么一个过程,老师都会用一个记事本给你记下来,就是把你这个交换记录给你作为一个logs的形式给你记下来,那记录比如A小朋友从B小朋友里面拿到了什么样的积木,是拿了一个圆形的还是拿了一个正方形的等等,所以这个老师手里这个记事本就叫做一个lineage,这个lineage就是类似于老师来记录小朋友之间交换积木的这么一个记录,它用来记录这个logs,然后这个时候每一个小朋友它们的目的就是把自己完成的这个工作最终交给老师,然后老师对他们的工作进行一些个打分点评对吧?
并且把各自的结果进行保存,所以对于spark任务来说,最终的目标是Actions,那具体什么Actions呢?
这个就具体情况来说了,所以对于spark这种任务必须有一个Actions,必须要有一个目标,你这个Actions比如说把这个小朋友他们自己的成果保存起来(save),也有可能是给大家做一个展示也就是collect,这都是一些个算子把,好了如果在这个交换过程中有一些小朋友或者有一些个task他不太满意自己的分配,那就会要求老师做一些个回滚,那他想回滚这个任务那依赖于什么呢?
就依赖于你这个lineage这个记录,否则你也不知道你这个积木你借过来了,我想把这个数据回滚,那我这个数据应该交还给谁是吧?
所以你必须要有这个记录来跟着的,所以这个lineage主要的目的就是来做一个容错,来防止你的数据丢失。
通常在不同的机器上,比如说对于你这个数据,想对这个数据做一个容错的话,最容易想到的点就是说把你的数据进行备份,但是数据备份这一个事情会必定涉及到更多的资源,那spark也会用到这样的机制,但是更多的时候它得分情况,比如通常对于那些展依赖关系的那些数据,它是可以通过很快速的计算能够可以生成,比如说下图18所示
图18:
输入数据到了2这个地方了,我这个数据就出现了一些意外导致2身上拿到的这个数据丢失了,那如果要是说从这个input输入通过很多的流程到2的过程中都是那种算子进行连接的话,那这个数据完全是可以通过每一层的数据计算还原回来的,就是没有必要说我在这个2这里面我提前把这个数据备份好,然后我再去做计算,那如果要是做计算发现我的数据出问题了或者任务出问题了,那我这个数据因为我刚才备份好了,那直接拿过来再来用,那这样的话势必会影响你的存储性能,那在spark里面内部更多的是数据通过一些还原的方式来去完成数据的恢复,那对于窄依赖的时候你的数据恢复是会很快的,那对于宽依赖来说你的数据恢复是很慢的,所以通常来说也不是说所以的数据都必须通过还原的形式,通过从前到后每一个环节都要还原再计算一遍得到,那为了能够节省时间或者提高性能,那通常来说对于宽依赖的这些算子因为它好不容易得到这个数据,因为它需要依赖于上游所有的节点都给你准备就绪,它这个数据才能够得到。
那一旦我好不容易得到这个数据丢失了,那怎么办?
相当于我上游所有的算子全部同时在帮我再去计算然后再给我,所以这个过程中很花时间的对吧?
所以对于那种宽依赖的算子来说通常它得到这个数据是很重要很宝贵的,那对于这种宝贵的数据那通常的优化方法是把宽依赖得到的这个数据要做一次缓存备份,但是对于窄依赖的那些数据是不需要备份的,因为它可以很快速的得到,因为你这个RDD得到的数据是通过前面的一些变换得到的,那这些变换都是通过一些算子来去前后之间的联系计算得到,那如果要是说出现一些数据丢失了,那因为我RDD是可以能够模拟出整个的计算流程,那我就完全可以知道这部分数据是有哪一条链路执行过来的,那这个时候我就可以通过重新计算把丢失的数据追诉回来就可以了,那特别是对于这种窄依赖恢复数据的方法会很快,那如果对于宽依赖来说,就恢复的很慢的,但是如果你要宽依赖不去做缓存,那数据丢失了,当然它也可以去做一些回数,但是这个过程会比较慢一点。
那刚才说它这个RDD有一个懒惰性,就是对于这里面的一个懒操作,就是延迟计算,就是说我在整个过程中要经历很多个Transformations的一些算子,但是我每经历过这些Transformations算子我都不会去真正的执行,那我只有遇到一个Actions的时候我才把我之前前面所有算子才开始执行一遍,所以对于RDD来说它是能不马上计算就不马上计算,它能拖就拖对吧?
一直拖到有Actions,它才躲不过去了,那没办法那把前面所有欠的债全都一并给它还上,就是这么一个过程。
然后瞬时性,就是这个RDD得到这个数据之后,就会给后面下游进行一些个输送,那数据输送出去之后,这个RDD如果你没有cache的话,那这个RDD里面的数据就自然会去消失掉,就是用完就会释放,这样的话不会能够让你的内存能够得到更多的节省。
那创建RDD的方法大概有四种(如下图19所示)
图19:
第一种就是从入口中来,上图19中的sc就是sparkcontext,是spark的入口,编写spark程序用到的第一个类,就包含这么一个对象,这个对象可以去维护一些配置,一些环境变量等等,相当于通过这样的方式得到的这个a就是一个RDD。
然后还有一种RDD是通过其他RDD转换过来的,比如说b的RDD就是通过a经过一系列
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- spark 理论 笔记
![提示](https://static.bdocx.com/images/bang_tan.gif)