形象理解大数据:hadoop、hdfs、mapreduce、yarn、zookeeper、hive、spark

大数据生态

在这里插入图片描述
在这里插入图片描述

Hadoop-hdfs-HA高可用

从前有个村子(集群),叫大树局村(大数据)。村里有6户人家(节点),分别是老哈家(H),老阿家(A),老得家(D),大欧家(O),小欧家(O),大坡家(P)。他们几家都有通道相连(内网)。村长是老哈。老哈一直想着村子得干点什么,所以他决定先把村子做成一个大仓库。能够存取东西,还能查东西。
所以他就请来了一个仓库管理员,呢呢(NameNode)。呢呢有点像机器人总动员里的瓦力,而且总是说’我有好多粉丝呢‘(HDFS)。村长担心一个要是这个呢呢生病了,那不就歇菜了,所以又拉过来一个呢呢。平时一个呢呢工作(active),一个呢呢休息(standby)。
呢呢在每户家里都配了一个滑板机器人(DataNode),负责复制搬运存放货物的箱子。滑板机器人会定时和呢呢联系,同时向他们两个发送报告,但是只有工作中的呢呢有一个可以回答的头戴式对讲机,因此滑板机器人只接受工作中的呢呢的指令。
滑板机器人之间都带着手拿式对讲机,可以和其他的滑板机器人联系,谈论一下箱子的处理问题。箱子一般是长宽高为128cm的透明结构,但它会根据货物的体积变的可大可小。
来了一批货,呢呢指挥大家先把这些货物分成128cmX128cmX128cm的小块,然后放进箱子里,每个箱子复制两个,将他们分别放到不同的家里,为的就是不丢失。
呢呢先把货物的进出变化情况通过远程笔写到胶囊管道里的记录本本上(edits),然后 再根据这个本本把这批货和箱子的对应情况写到一张表上(fsimage),平时它把这张表放在它肚子里的空间中(内存中)。但是这个本子并不在呢呢身上,它在胶囊管道里。
这六户人家的屋顶都有一个管道通向上面一个类似于通风管的管道(journalNode共享空间),这就是胶囊管道。两个呢呢都有一副眼镜可以看到胶囊管道里本本上的内容,但是只有工作的呢呢可以使用那只记录笔。
但是村长怎么知道工作的呢呢生病了呢?于是它想到了动物园长(zookeeper),它那里有监听心跳的红绿控制灯(ZKFC)。工作的呢呢,胸前的控制灯是绿色的,休息的呢呢胸前的红绿灯是红色的。要是工作的呢呢突然晕倒了,那么控制灯会立刻把这一情况报告给动物园长,动物园长就从带红灯的呢呢中选出一个变绿。 同时头戴式对讲机和记录笔也会交到选出来的呢呢手中。新上任的呢呢赶紧根据共享管道中的记录本及时更新肚子里的对应表。
在这里插入图片描述

NameNode节点:两种分别在不同的节点上,active和standby。
			负责管理文件系统的命名及客户端对文件的访问,也就是中心服务器,读写文件。
	元数据:元数据保存在namenode的内存中,以便快速查询,主要包括fsimage和edits。
		fsimage:元数据镜像文件(保存文件系统的目录树)。
		edits:元数据操作日志(针对目录树的修改操作)被写入共享存储系统中,比如journalnode,内存中保存一份最新的元数据镜像(fsimage和edits)。
DataNode节点:在NameNode节点的统一调动下,负责处理文件系统对客户端的读写请求,或删除、创建、复制数据块等。定时和namenode进行通信,接受namenode指令,同时datanode之间还会相互通信,执行数据块复制任务。块报告发送给所有的namenode,但是只服从activeNN的命令。
hdfs执行原理:namenode接受客户端读写服务请求,并根据它保存的Metadata元数据,包括元数据的镜像文件(fsimage 		和操作日志edits信息)和datanode通信并进行资源协调。如果要存储大文件,首先将大文件分割成块,分别放到不同的节点,每块文件都有三个副本备份,并且有一个专门记录文件块存放位置的元数据文件以备查询。
zkfc:ZooKeeper FailoverController节点用于监控和控制namenode的状态切换,当一个集群中的active namenode挂掉后,会把standby namenode状态切换为active。FailoverController Active和FailoverController Standby分别对active nn和standby nn进行健康检查,把心跳数据传给zookeeper。如果active nn掉线了,zookeeper会进行选举,选举出来后,由FailoverController进行切换。
ZooKeeper:选择其中一个备用的namenode为active。
# 问题:
块大小以及为何这样设置?
hadoop1为64M,hadoop2和hadoop3为128M。专家称寻址时间为传输时间的1%为最佳状态。寻址时间为10ms,传输时间为1m,目前磁盘的传输速率普遍为100MB/s。HDFS块设置太小,会增加寻址时间,设置太大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需要的时间。导致程序处理数据变慢。
HDFS块的大小设置主要取决于磁盘传输速率。

MapReduce

货物存好后,要是客户想要计算一下货物的数据咋办?瓦力干不了这活。所以村长找来了小火车(MapReduce)。它一共有四节,第一节车厢上有一把刀(split),会把货物切成多块。第二节车厢有很多写着‘1’的纸条,它会在每一块的最小物件上贴个纸条(map)。第三节车厢上有一些纸箱子,它能把同样的物件收到一个箱子里(shuffle),最后一个车厢有个计算器(reduce),计算统计每个箱子有多少物件。
小火车车头有个车长,来了一批货,先经过第一节车厢,大刀会按照透明箱子的大小来切。第二车厢会根据第一车厢的块数来决定配备多少贴纸条的贴机。
第三车厢前半部有一个圆形的实际使用空间只有80%的空心转盘(环形缓冲区),等里面的货物存满后,先进行分区,各个区的货物种类都不一样,然后每个区按照大小进行一次快速排序,最后在转盘下面打开口子排出。转盘底下是一个磁铁做成的方台(硬盘)。分区排好序的货物就一堆堆的先整齐列在磁台上,有小人会从所有的堆中挑选同一种货物放到一个纸箱子里。纸箱子放到车厢后面的磁台上。第四个车厢的计算器会根据第三个车厢的纸箱子进行复制,如果纸箱子太大,也把复制的纸箱子放到磁台上,要是箱子比较小,就先吊起来,等吊起来的纸箱子达到一定体积,就放下来。最后把磁盘上纸箱子进行合并排序,空中吊起的也进行合并排序,组后把所有的合并排序。

MapReduce:基于离线数据的分布式运算程序。
	split:默认切片大小等于块。
	map:有多少切片,就有多少MapTask。
	shffle:它会将MapTask处理的结果暂时放到环形缓冲区中,当环形缓冲区(默认100M)使
				用率达到一定阈值后,再对缓冲区中的数据进行一次快速排序,并将这些有序数
				据溢写到磁盘上,而当数据处理完毕后,它会对磁盘上所有文件进行归并排序。
				(1)MapTask 收集我们的 map()方法输出的 kv 对,放到内存缓冲区中
				(2)从内存缓冲区不断溢出本地磁盘文件,可能会溢出多个文件
				(3)多个溢出文件会被合并成大的溢出文件
				(4)在溢出过程及合并的过程中,都要调用 Partitioner 进行分区和针对 key 进行排序
				(5)ReduceTask 根据自己的分区号,去各个 MapTask 机器上取相应的结果分区数据
				(6)ReduceTask 会抓取到同一个分区的来自不同 MapTask 的结果文件,
						 ReduceTask 会将这些文件再进行合并(归并排序)
				(7)合并成大文件后,Shuffle 的过程也就结束了,后面进入 ReduceTask 的逻辑运算过
						 程(从文件中取出一个一个的键值对 Group,调用用户自定义的 reduce()方法)。在·
	reduce:对于ReduceTask,它从每个MapTask上远程拷贝相应的数据文件,如果文件大
				  小超过一定阈值,则溢写磁盘上,否则存储在内存中。如果磁盘上文件数目达到
			      一定阈值,则进行一次归并排序以生成一个更大文件;如果内存中文件大小或者
			      数目超过一定阈值,则进行一次合并后将数据溢写到磁盘上。当所有数据拷贝完
			     毕后,ReduceTask统一对内存和磁盘上的所有数据进行一次归并排序。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Yarn

小火车虽然很厉害,但归根结底还是一个工具。找谁来管理小火车呢?村长想起了鸭安(Yarn)。鸭安不是一只鸭子,而是一群鸭子加一个划线机,为首的有两个,一公一母。公鸭(ResourceManager)负责接受业务,监控小鸭(NodeManager),叫醒并监控母鸭(ApplicationMaster),还有工具和地的分配。母鸭负责为任务申请一些资源工具,并监控这些任务及时纠错。小鸭负责管理每家每户的资源,并听从公鸭和母鸭的命令。
有客户(YarnRunner)要查某项业务,首先向公鸭申请一个纸条(Application),纸条上写着这个任务的编号和要把所需的东西放在各家各户的位置(应用程序的资源路径)。客户拿到纸条后就把它带来的东西交给瓦力,瓦力根据纸条上的编号地址,把东西放到对应的位置(HDFS )。然后客户就向公鸭申请给我配一个母鸭(mrAppMaster)吧。公鸭收到请求后,把那个有任务编号的纸条副本塞进一个蛋里(Task),然后放到一个管道里(容量调度器)。哪个小鸭闲了把这个蛋领走,然后用划线机划一片区域(容器 Container)给这个蛋。画好区域后,这个蛋里就蹦出来个母鸭(MRAppmaster),它手里拿着任务编号纸条,然后根据路径把所需的东西拉来(Container 从 HDFS 上拷贝资源到本地)。然后母鸭看了看任务清单,然后向公鸭申请多个区域执行第一步任务(MRAppmaster 向 RM 申请运行 MapTask 资源)。公鸭收到申请后,指派了两个小鸭来完成这第一步任务(RM 将运行 MapTask 任务分配给另外两个 NodeManager),两个小鸭接到任务后,赶紧用划线机各划出一片区域来(另两个 NodeManager 分别领取任务并创建容器)。然后,母鸭就把任务开始以及过程步骤发给两个小鸭(MR 向两个接收到任务的 NodeManager 发送程序启动脚本),小鸭就按照步骤来执行任务(这两个 NodeManager分别启动 MapTask,MapTask 对数据分区排序)。等到两个小鸭都执行完任务后,母鸭向公鸭再申请一块地执行第二步任务。公鸭于是又指派了两个小鸭来完成第二步任务,这两个小鸭划出区域后就根据各自领到的分区路径拿去上一步加工出来的货物,然后执行任务,最后任务执行完毕, 母鸭会向公鸭申请变成一个蛋(MrAppMaster 等待所有 MapTask 运行完毕后,向 RM 申请容器,运行 ReduceTask)。
公鸭身上的管道是个什么东西呢?它有点像分歧终端机。公鸭身上有多个管道,长短不一。蛋来了就扔进一个管道里,先进先出。

Yarn:资源调度,应用程序管理框架。
(1)MR 程序提交到客户端所在的节点。
(2)YarnRunner 向 ResourceManager 申请一个 Application。
(3)RM 将该应用程序的资源路径返回给 YarnRunner。
(4)该程序将运行所需资源提交到 HDFS 上。
(5)程序资源提交完毕后,申请运行 mrAppMaster。
(6)RM 将用户的请求初始化成一个 Task。
(7)其中一个 NodeManager 领取到 Task 任务。
(8)该 NodeManager 创建容器 Container,并产生 MRAppmaster。
(9)Container 从 HDFS 上拷贝资源到本地。
(10)MRAppmaster 向 RM 申请运行 MapTask 资源。
(11)RM 将运行 MapTask 任务分配给另外两个 NodeManager,另两个 NodeManager 分
别领取任务并创建容器。
(12)MR 向两个接收到任务的 NodeManager 发送程序启动脚本,这两个 NodeManager
分别启动 MapTask,MapTask 对数据分区排序。
(13)MrAppMaster 等待所有 MapTask 运行完毕后,向 RM 申请容器,运行 ReduceTask。
(14)ReduceTask 向 MapTask 获取相应分区的数据。
(15)程序运行完毕后,MR 会向 RM 申请注销自己。

在这里插入图片描述
在这里插入图片描述

作业提交全过程详解
(1)作业提交
第 1 步:Client 调用 job.waitForCompletion 方法,向整个集群提交 MapReduce 作业。
第 2 步:Client 向 RM 申请一个作业 id。
第 3 步:RM 给 Client 返回该 job 资源的提交路径和作业 id。
第 4 步:Client 提交 jar 包、切片信息和配置文件到指定的资源提交路径。
第 5 步:Client 提交完资源后,向 RM 申请运行 MrAppMaster。
(2)作业初始化
第 6 步:当 RM 收到 Client 的请求后,将该 job 添加到容量调度器中。
第 7 步:某一个空闲的 NM 领取到该 Job。
第 8 步:该 NM 创建 Container,并产生 MRAppmaster。
第 9 步:下载 Client 提交的资源到本地。
(3)任务分配
第 10 步:MrAppMaster 向 RM 申请运行多个 MapTask 任务资源。
第 11 步:RM 将运行 MapTask 任务分配给另外两个 NodeManager,另两个 NodeManager
分别领取任务并创建容器。
(4)任务运行
第 12 步:MR 向两个接收到任务的 NodeManager 发送程序启动脚本,这两个
NodeManager 分别启动 MapTask,MapTask 对数据分区排序。
第13步:MrAppMaster等待所有MapTask运行完毕后,向RM申请容器,运行ReduceTask。
第 14 步:ReduceTask 向 MapTask 获取相应分区的数据。
第 15 步:程序运行完毕后,MR 会向 RM 申请注销自己。
(5)进度和状态更新
YARN 中的任务将其进度和状态(包括 counter)返回给应用管理器, 客户端每秒(通过
mapreduce.client.progressmonitor.pollinterval 设置)向应用管理器请求进度更新, 展示给用户。
(6)作业完成
除了向应用管理器请求作业进度外, 客户端每 5 秒都会通过调用 waitForCompletion()来
检查作业是否完成。时间间隔可以通过 mapreduce.client.completion.pollinterval 来设置。作业
完成之后, 应用管理器和 Container 会清理工作状态。作业的信息会被作业历史服务器存储
以备之后用户核查。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

ZooKeeper

那位动物园长又是何许人也?它就是翠神艾翁。它有四条根须。村长想让帮助处理仓库事宜,那么首先要在艾翁那登记注册一下每家每户的配置信息(统一管理配置),它会给每家每户起一个名字(统一命名服务),然后到家里去安装监控和显示屏。通过监控,艾翁能知道哪些家里出了事情,哪些家里没人(服务器节点动态上下线),哪些家里能够做事情(软负载均衡)。当然,村里哪些东西需要发生变化,艾翁会通过显示屏告诉各家各户(统一管理配置)。
艾翁有四个口袋,第一个口袋里放着起名本(统一命名),第二个口袋放着电话(配置管理),第三个口袋放着监控器(实时监控),第四个口袋放着选举落定锤(负载均衡)。腰带后还别个锁(分布式锁),如果哪家要处理一批货物,艾翁就会给这家一个锁,把这批正在处理的货物锁住。处理完成后再把锁还给艾翁。

ZooKeeper:分布式协调服务。它负责存储和管理大家都关心的数据,然后接受观察者的注册,
					  一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的
					  那些观察者做出相应的反应。统一命名、配置管理、实时监控、负载均衡、分布式锁。
	统一命名服务:在分布式环境下,对应用/服务进行统一命名,便于识别。
	统一管理配置:将集群配置信息写入Znode,各个客户端服务区监听它,若配置信息修改,
							zookeeper通知各个服务器。
	统一集群管理:将节点信息写入一个Znode,监听这个Znode获取节点实时状态,做出一些调整。
	服务器节点动态上下线:实时洞察服务器上下线变化。
	软负载均衡:在zookeeper中记录每台服务器访问数,让访问次数最少的处理最新的客户端请求。
	分布式锁:进程1使用某资源的时候,会先去获得锁,获得锁之后,进程1就对该资源保持独占,
					  其他进程无法访问该资源。处理完成后,将锁释放掉,让其他进程来获得锁。
					  通过锁机制能保证分布式系统中多个进程能够有序的访问该临界资源。
					  这个分布式环境下的锁叫分布式锁。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Hive

虽然有小火车和鸭安,但客户还是有一些抱怨,因为他们要搞一项业务,要自己写很多步骤流程,尤其是小火车的组装那块,哪个任务用哪个车厢,怎么搭配,非常的麻烦。
村长听到客户的抱怨,想到了变形金刚霸天虎(Hive)。它可以把客户的业务需求变成小火车车厢。霸天虎有三个地方可以接受客户的请求,头部(CLI),还有两边肩膀上的肩章(JDBC/ODBC),头部是一个显示器(WEBUI),上面可以直接输入命令。左边肩章是加多宝茶,右边肩章上是chorme标志。
霸天虎的肩膀是一排马达(驱动器Driver),马达下面有三列铁门,第一第二列只有一个铁门,第三列有上下两个铁门。第一列铁门(Sql Parse解析器)内的工具会把客户提的需求变成一个任务树,当然客户也可以自己设计一些小工具放到里面。第二列铁门(Physical Plan编译器)内的工具会把生成的任务树拆成零碎的物件。第三列上部分铁门(Query Optimizer优化器)内的工具对拆分的零件进行精雕细琢,第三列下部分铁门(Execution执行器)内的工具会把打磨好的物件拼成小火车车厢。最后霸天两个脚就是小火车车厢(MapReduce)。
霸天虎还有个双层盾牌(Metas tore)。外层盾牌上只能写一些东西(外部表),内层盾牌是空心的(内部表),表面不仅可以写东西,里面可以装一些东西。两层盾牌都可以平行分成几行(表分区),每一行都可以画一些桶(表分桶)。

Hive:一种数据仓库,将结构化的数据文件映射为一张数据库表。 通过给用户提供的一系列交互接口,
			接收到用户的指令(SQL),使用自己的 Driver,结合元数据(MetaStore),将这些指令翻译成 MapReduce,
			提交到 Hadoop 中执行,最后,将执行返回的结果输出到用户交互接口。
	1)用户接口:Client
		CLI(command-line interface)、JDBC/ODBC(jdbc 访问 hive)、WEBUI(浏览器访问 hive)
	2)元数据:Metastore
		元数据包括:表名、表所属的数据库(默认是 default)、表的拥有者、列/分区字段、
		表的类型(是否是外部表)、表的数据所在目录等;
		默认存储在自带的 derby 数据库中,推荐使用 MySQL 存储 Metastore
	3)Hadoop
		使用 HDFS 进行存储,使用 MapReduce 进行计算。
	4)驱动器:Driver
	(1)解析器(SQL Parser):将 SQL 字符串转换成抽象语法树 AST,这一步一般都用第
									三方工具库完成,比如 antlr;对 AST 进行语法分析,比如表是否存在、
									字段是否存在、SQL语义是否有误。
	(2)编译器(Physical Plan):将 AST 编译生成逻辑执行计划。
	(3)优化器(Query Optimizer):对逻辑执行计划进行优化。
	(4)执行器(Execution):把逻辑执行计划转换成可以运行的物理计划。对于 Hive 来
													说,就是 MR/Spark。
外部表:创建时仅仅记录数据所在的位置,删除时仅仅删除元数据,不会删除真正的数据。
内部表:创建时会把数据移动到数据仓库所指向的位置,删除时会将元数据和数据一起删除。
表分区:分目录,针对的是数据的存储路径。
表分桶:分数据集,针对的是数据文件。

在这里插入图片描述

Spark

虽然解决了客户自己写步骤的问题,但客户又提出了一个新的问题,就是慢,想要办一项业务,等的时间太久了。他们向村长提出这个问题,村长觉得慢主要是货物在磁盘上(硬盘)搬来搬去引起的,而且地面上的工作进度一般。所以村长想到了飞机(Spark)。
飞机一共由五部分组成,头部(sql)、中间(core)、尾部(streaming)、左翅(mllib)、右翅(graphx)。头部画着霸天虎,中间是个引擎,尾巴是类似于鱼尾。左翅画着一个机器,右翅上画着个类似于北斗七星的图案。

spark:由scala语言开发的大数据分析引擎。
	spark core:spark最基础最核心的功能。
	spark SQL:操作结构化数据的组件,可以使用sql或Hql。
	spark Streaming:针对实时数据进行流式计算的组件。
	spark MLlilb:机器学习算法库。
	spark GraphX:面向图计算提供的框架与算法库。
从计算的角度来讲,数据处理过程中需要计算资源(内存 & CPU)和计算模型(逻辑)。
执行时,需要将计算资源和计算模型进行协调和整合。
Spark 框架在执行时,先申请资源,然后将应用程序的数据处理逻辑分解成一个一个的
计算任务。然后将任务发到已经分配资源的计算节点上, 按照指定的计算模型进行数据计
算。最后得到计算结果。

飞机其实可以自己工作(local模式),也可以用自己的工作人员来执行任务(standalone模式),但还是和鸭安配合起来效率比较高(Yarn模式)。
在这里插入图片描述

➢ Spark 查看当前 Spark-shell 运行任务情况端口号:4040(计算)
➢ Spark Master 内部通信服务端口号:7077
➢ Standalone 模式下,Spark Master Web 端口号:8080(资源)
➢ Spark 历史服务器端口号:18080
➢ Hadoop YARN 任务运行情况查看端口号:8088

大飞机是怎么提高效率的呢?先来看看大飞机最核心的部分,引擎。引擎里住着一只母鸡(driver)和一群小鸡(Executor)。

 Driver:Spark 驱动器节点,用于执行 Spark 任务中的 main 方法,负责实际代码的执行工作。
	Driver 在 Spark 作业执行时主要负责:
	➢ 将用户程序转化为作业(job)
	➢ 在 Executor 之间调度任务(task)
	➢ 跟踪 Executor 的执行情况
	➢ 通过 UI 展示查询运行情况
	实际上,我们无法准确地描述 Driver 的定义,因为在整个的编程过程中没有看到任何有关
	Driver 的字眼。所以简单理解,所谓的 Driver 就是驱使整个应用运行起来的程序,也称之为
	Driver 类。
Executor:	Spark Executor 是集群中工作节点(Worker)中的一个 JVM 进程,负责在 Spark 作业
			中运行具体任务(Task),任务彼此之间相互独立。Spark 应用启动时,Executor 节点被同
			时启动,并且始终伴随着整个 Spark 应用的生命周期而存在。如果有 Executor 节点发生了
			故障或崩溃,Spark 应用也可以继续执行,会将出错节点上的任务调度到其他 Executor 节点
			上继续运行。
	Executor 有两个核心功能:
	➢ 负责运行组成 Spark 应用的任务,并将结果返回给驱动器进程
	➢ 它们通过自身的块管理器(Block Manager)为用户程序中要求缓存的 RDD 提供内存
	式存储。RDD 是直接缓存在 Executor 进程内的,因此任务可以在运行时充分利用缓存
	数据加速运算。

客户有了任务,先召唤出母鸡,母鸡把客户的任务执行计划写到很多弹弹冻(RDD)上,然后去公鸭那里申请一个任务编号纸条。当然同时也可以申请一个母鸭(ApplicationMaster)去管理接下来的执行情况(Yarn Client 模式)。要是这样的话,公鸭会让一个小鸭用划线机划一块区域来,然后母鸭到那片区域,随后向公鸭申请一些高楼层给小鸡。公鸭接到母鸭申请后会再让其他小鸭划几个区域给小鸡们。然后小鸡们就进入工作区域。小鸡们到位后会向通知母鸡已经到啦,等所有小鸡们都通知完了,母鸡开始梳理一下弹弹冻,梳理到最后,划分成几大部分,每一部分生成的小算盘(算子),都发给小鸡们去做。

Yarn Client 模式:Client 模式将用于监控和调度的 Driver 模块在客户端执行,而不是在 Yarn 中,所以一
				般用于测试。
	➢ Driver 在任务提交的本地机器上运行
	➢ Driver 启动后会和 ResourceManager 通讯申请启动 ApplicationMaster
	➢ ResourceManager 分配 container,在合适的 NodeManager 上启动 ApplicationMaster,负
		责向 ResourceManager 申请 Executor 内存
	➢ ResourceManager 接到 ApplicationMaster 的资源申请后会分配 container,然后
		ApplicationMaster 在资源分配指定的 NodeManager 上启动 Executor 进程
	➢ Executor 进程启动后会向 Driver 反向注册,Executor 全部注册完成后 Driver 开始执行
	main 函数
	➢ 之后执行到 Action 算子时,触发一个 Job,并根据宽依赖开始划分 stage,每个 stage 生
	成对应的 TaskSet,之后将 task 分发到各个 Executor 上执行。

当然实际上,母鸡是不放心小鸡们的,它不愿自己呆在飞机上。
它还是到鸭安的地方去指导比较放心(Yarn Cluster模式)。这样的话,母鸡向公鸭申请的就是任务编号纸条,并且自己要代替母鸭的角色。公鸭接到申请找一个合适的小鸭,让它划一片区域,母鸡到达这片区域后,再向公鸭申请划更多的高楼层。公鸭随后安排其它小鸭划出高层区域,让小鸡上去。小鸡到达后向母鸡报告,等所有小鸡都报告完毕,母鸡开始梳理弹弹冻。梳理到最后,母鸡把弹弹冻分成几大部分,每个部分的小算子分到不同的小鸡手里,它们就开始工作起来。

Yarn Cluster 模式:Cluster 模式将用于监控和调度的 Driver 模块启动在 Yarn 集群资源中执行。一般应用于
	实际生产环境。
	➢ 在 YARN Cluster 模式下,任务提交后会和 ResourceManager 通讯申请启动ApplicationMaster,
	➢ 随后 ResourceManager 分配 container,在合适的 NodeManager 上启动 ApplicationMaster,
		此时的 ApplicationMaster 就是 Driver。
	➢ Driver 启动后向 ResourceManager 申请 Executor 内存,ResourceManager 接到
		ApplicationMaster 的资源申请后会分配 container,然后在合适的 NodeManager 上启动
		Executor 进程
	➢ Executor 进程启动后会向 Driver 反向注册,Executor 全部注册完成后 Driver 开始执行main 函数,
	➢ 之后执行到 Action 算子时,触发一个 Job,并根据宽依赖开始划分 stage,每个 stage 生
		成对应的 TaskSet,之后将 task 分发到各个 Executor 上执行。

在这里插入图片描述
那个神秘的弹弹冻是什么东西的?准确的说,它是由很多东西组成的。其中主体是若干片淡蓝色的半透明状的长方形果冻状立方体(RDD)。它能在地面和高楼间跳来跳去(存储的弹性),掉了一块也能自动复原(容错的弹性),变身不理想也能恢复原状(计算的弹性),能够根据需要随意切分(分片的弹性)。一片弹弹冻可以分成多个层区,至于怎么分呢,靠放大镜(哈希分区)和直尺(range分区)。
弹弹冻的每一层的背面都嵌有一些算子。算子说白了就是算盘的零件。算子大体上分两种,转换算子和行动算子。转换算子有算珠(value)和串联算珠的金属棍(key)。行动算子是中间有绳网连接的算盘框。转换算子一共有三种搭配,单个算珠(Value 类型),两个算珠(双 Value 类型),一个金属棍加一个算珠(Key-Value类型)。有货物会经过弹弹冻透明区域,穿过O形算珠,OO形算珠,1O形柱算珠,到下一块弹弹冻上。最后一块弹弹冻的背面就是绳网方框,货物最后跳到这个方框上被弹出去。

RDD:弹性分布式数据集。一种抽象类,代表了一个弹性的、不可变的、可分区的里面的元素可并行计算的集合。
	RDD是计算模型的封装,当需求中需要将多个计算模型进行组合时,就需要将多个 RDD 建立依赖关系。
	RDD 在整个流程中主要用于将逻辑进行封装,并生成 Task 发送给Executor 节点执行计算。
	➢ 弹性
	⚫ 存储的弹性:内存与磁盘的自动切换;
	⚫ 容错的弹性:数据丢失可以自动恢复;
	⚫ 计算的弹性:计算出错重试机制;
	⚫ 分片的弹性:可根据需要重新分片。
	➢ 分布式:数据存储在大数据集群不同节点上
	➢ 数据集:RDD 封装了计算逻辑,并不保存数据
	➢ 数据抽象:RDD 是一个抽象类,需要子类具体实现
	➢ 不可变:RDD 封装了计算逻辑,是不可以改变的,想要改变,只能产生新的 RDD,在
	新的 RDD 里面封装计算逻辑
	➢ 可分区、并行计算
Spark 可以将一个作业切分多个任务后,发送给 Executor 节点并行计算,而能
够并行计算的任务数量我们称之为并行度。这个数量可以在构建 RDD 时指定。记住,这里
的并行执行的任务数量,并不是指的切分任务的数量,不要混淆了。

Spark 目前支持 Hash 分区和 Range 分区,和用户自定义分区。Hash 分区为当前的默认
分区。分区器直接决定了 RDD 中分区的个数、RDD 中每条数据经过 Shuffle 后进入哪个分
区,进而决定了 Reduce 的个数。
➢ 只有 Key-Value 类型的 RDD 才有分区器,非 Key-Value 类型的 RDD 分区的值是 None
➢ 每个 RDD 的分区 ID 范围:0 ~ (numPartitions - 1),决定这个值是属于那个分区的。
1) Hash 分区:对于给定的 key,计算其 hashCode,并除以分区个数取余
2) Range 分区:将一定范围内的数据映射到一个分区中,尽量保证每个分区数据均匀,而
且分区间有序

Spark分区 partition 详解

转换算子
RDD 根据数据处理方式的不同将转换算子整体上分为 Value 类型、双 Value 类型和 Key-Value类型。
Value 类型:
	map:将处理的数据逐条进行映射转换,这里的转换可以是类型的转换,也可以是值的转换。
	mapPartitions:将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处
					理,哪怕是过滤数据。
	mapPartitionsWithIndex:将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处
					理,哪怕是过滤数据,在处理时同时可以获取当前分区索引。
	flatMap:将处理的数据进行扁平化后再进行映射处理,所以算子也称之为扁平映射。
	glom:将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变。
	groupBy:将数据根据指定的规则进行分组, 分区默认不变,但是数据会被打乱重新组合,我们将这样
					的操作称之为 shuffle。极限情况下,数据可能被分在同一个分区中。
	filter:将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。
			当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出
			现数据倾斜。
	sample:根据指定的规则从数据集中抽取数据。
	distinct:将数据集中重复的数据去重。
	coalesce:根据数据量缩减分区,用于大数据集过滤后,提高小数据集的执行效率
					当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少
					分区的个数,减小任务调度成本。
	repartition:该操作内部其实执行的是 coalesce 操作,参数 shuffle 的默认值为 true。无论是将分区数多的
				RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition
				操作都可以完成,因为无论如何都会经 shuffle 过程。
	sortBy:该操作用于排序数据。在排序之前,可以将数据通过 f 函数进行处理,之后按照 f 函数处理
			的结果进行排序,默认为升序排列。排序后新产生的 RDD 的分区数与原 RDD 的分区数一
			致。中间存在 shuffle 的过程。
双 Value 类型:
	intersection:对源 RDD 和参数 RDD 求交集后返回一个新的 RDD。
	union:对源 RDD 和参数 RDD 求并集后返回一个新的 RDD。
	subtract:以一个 RDD 元素为主,去除两个 RDD 中重复元素,将其他元素保留下来。求差集。
	zip:将两个 RDD 中的元素,以键值对的形式进行合并。其中,键值对中的 Key 为第 1 个 RDD
		中的元素,Value 为第 2 个 RDD 中的相同位置的元素。
Key - Value 类型:
	partitionBy:将数据按照指定 Partitioner 重新进行分区。Spark 默认的分区器是 HashPartitioner。
	reduceByKey:可以将数据按照相同的 Key 对 Value 进行聚合。
	groupByKey:将数据源的数据根据 key 对 value 进行分组。
				从 shuffle 的角度:reduceByKey 和 groupByKey 都存在 shuffle 的操作,但是 reduceByKey
				可以在 shuffle 前对分区内相同 key 的数据进行预聚合(combine)功能,这样会减少落盘的
				数据量,而 groupByKey 只是进行分组,不存在数据量减少的问题,reduceByKey 性能比较
				高。
				从功能的角度:reduceByKey 其实包含分组和聚合的功能。GroupByKey 只能分组,不能聚
				合,所以在分组聚合的场合下,推荐使用 reduceByKey,如果仅仅是分组而不需要聚合。那
				么还是只能使用 groupByKey
	aggregateByKey:将数据根据不同的规则进行分区内计算和分区间计算。
	foldByKey:当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为 foldByKey。
	combineByKey:最通用的对 key-value 型 rdd 进行聚集操作的聚集函数(aggregation function)。类似于
			aggregate(),combineByKey()允许用户返回值的类型与输入不一致。	
	sortByKey:在一个(K,V)的 RDD 上调用,K 必须实现 Ordered 接口(特质),返回一个按照 key 进行排序。
	join:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的所有元素连接在一起的
			(K,(V,W))的 RDD。
	leftOuterJoin:类似于 SQL 语句的左外连接
	cogroup:在类型为(K,V)和(K,W)的 RDD 上调用,返回一个(K,(Iterable<V>,Iterable<W>))类型的 RDD。
	
行动算子:
reduce:聚集 RDD 中的所有元素,先聚合分区内数据,再聚合分区间数据。
collect:在驱动程序中,以数组 Array 的形式返回数据集的所有元素。
count:返回 RDD 中元素的个数。
first:返回 RDD 中的第一个元素
take:返回一个由 RDD 的前 n 个元素组成的数组。
takeOrdered:返回该 RDD 排序后的前 n 个元素组成的数组。
aggregate:分区的数据通过初始值和分区内的数据进行聚合,然后再和初始值进行分区间的数据聚合。
fold:折叠操作,aggregate 的简化版操作。
countByKey:统计每种 key 的个数。
save 相关算子:将数据保存到不同格式的文件中。
foreach:分布式遍历 RDD 中的每一个元素,调用指定函数。

货物跳来跳去,那丢了怎么办呀?所以有一个类似于族谱的本子(Lineage)会记录下这些货物来自哪,变成了什么,蹦哪去了。要是一个弹弹冻其中一层的一个货物只能蹦到下一个弹弹冻的其中一层,那这两个弹弹冻之间的距离就比较近(RDD 窄依赖),要是能蹦到多层,那么这两个弹弹冻之间的距离比较宽(RDD 宽依赖)

RDD 只支持粗粒度转换,即在大量记录上执行的单个操作。将创建 RDD 的一系列 Lineage
(血统)记录下来,以便恢复丢失的分区。RDD 的 Lineage 会记录 RDD 的元数据信息和转
换行为,当该 RDD 的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的
数据分区。

RDD 窄依赖
	窄依赖表示每一个父(上游)RDD 的 Partition 最多被子(下游)RDD 的一个 Partition 使用,
	窄依赖我们形象的比喻为独生子女。
RDD 宽依赖
	宽依赖表示同一个父(上游)RDD 的 Partition 被多个子(下游)RDD 的 Partition 依赖,会
	引起 Shuffle,总结:宽依赖我们形象的比喻为多生。
	
RDD 任务切分中间分为:Application、Job、Stage 和 Task
⚫ Application:初始化一个 SparkContext 即生成一个 Application;
⚫ Job:一个 Action 算子就会生成一个 Job;
⚫ Stage:Stage 等于宽依赖(ShuffleDependency)的个数加 1;
⚫ Task:一个 Stage 阶段中,最后一个 RDD 的分区个数就是 Task 的个数。
注意:Application->Job->Stage->Task 每一层都是 1 对 n 的关系。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
货物有时并不是从一个弹弹冻直接跳到下一个弹弹冻的,可能还会通过某些途径。两个弹弹冻中间地面有个叉车(cache),上面吊着个绳子(persist),货物可以先跳到叉车上,也可以先挂到绳子上。

RDD 通过 Cache 或者 Persist 方法将前面的计算结果缓存,默认情况下会把数据以缓存
在 JVM 的堆内存中。但是并不是这两个方法被调用时立即缓存,而是触发后面的 action 算
子时,该 RDD 将会被缓存在计算节点的内存中,并供后面重用。
缓存有可能丢失,或者存储于内存的数据由于内存不足而被删除,RDD 的缓存容错机
制保证了即使缓存丢失也能保证计算的正确执行。通过基于 RDD 的一系列转换,丢失的数
据会被重算,由于 RDD 的各个 Partition 是相对独立的,因此只需要计算丢失的部分即可,
并不需要重算全部 Partition。
Spark 会自动对一些 Shuffle 操作的中间数据做持久化操作(比如:reduceByKey)。这样
做的目的是为了当一个节点 Shuffle 失败了避免重新计算整个输入。但是,在实际使用的时
候,如果想重用数据,仍然建议调用 persist 或 cache。

有时候两个弹弹冻之间还会摆上一块磁盘,因为货物跳的太远太长了,要是丢了就得从头跳了,所以,可以在某个阶段把跳下来的货物副本先摆到磁盘上(RDD CheckPoint 检查点)。

所谓的检查点其实就是通过将 RDD 中间结果写入磁盘
由于血缘依赖过长会造成容错成本过高,这样就不如在中间阶段做检查点容错,如果检查点
之后有节点出现问题,可以从检查点开始重做血缘,减少了开销。
对 RDD 进行 checkpoint 操作并不会马上被执行,必须执行 Action 操作才能触发。
缓存和检查点区别
1)Cache 缓存只是将数据保存起来,不切断血缘依赖。Checkpoint 检查点切断血缘依赖。
2)Cache 缓存的数据通常存储在磁盘、内存等地方,可靠性低。Checkpoint 的数据通常存
储在 HDFS 等容错、高可用的文件系统,可靠性高。
3)建议对 checkpoint()的 RDD 使用 Cache 缓存,这样 checkpoint 的 job 只需从 Cache 缓存
中读取数据即可,否则需要再从头计算一次 RDD。

母鸡把弹弹冻上以及上面的所有算子拆分后发给小鸡 ,最后拿到方框绳网的小鸡完成任务后,会把加工出来的货物放到一个有很多层的铁架车上(累加器),最后母鸡把这个铁架上的所有货物归纳一下,交给客户就算完成任务了。

累加器用来把 Executor 端变量信息聚合到 Driver 端。在 Driver 程序中定义的变量,在
Executor 端的每个 Task 都会得到这个变量的一份新的副本,每个 task 更新这些副本的值后,
传回 Driver 端进行 merge。

Spark性能调优–资源参数调优、算子调优、Shuffle参数调优、Spark 数据倾斜调优

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值