继上篇https://blog.csdn.net/Mirror_w/article/details/95783445
那么相应的实现是怎么实现的呢?
先说它的框架,在框架当中,mapreduce也好,Spark也好,资源层和计算才能是隔开的,是解耦的,在MapReduce当中,Yarn是资源层,然后自己的计算支持着计算层,比如说有客户端、有appMaster、有maptask reduce task都是支持着计算层,资源层就是resourcemanager和nodemanager。而在Spark当中,Spark比MapReduce更加强大,Spark有几种组合形式,它自己可以完成standalone模式,standalone中也会有一个资源层,资源层的角色分别是master和worker,Master和Worker是standalone的两个资源层层面的进程,Spark除了和自己的standalone模式及进行整合,还可以和Yarn进行整合,SparK on Yarn 模式,也就是把Master和Worker删掉,换成resourceManager和nodeManager,这种资源层,还可以换成mensos,等其他的资源层,无论是哪个资源层,它的计算层是不变的,如果计算层变的话,在不同的资源层上跑的时候,还得一直改代码。所以Spark中代码逻辑一次性写完之后可以跑在不同的资源层上,计算层是统一的,计算层是什么结构?计算层有一个客户端进程、Driver,负责资源申请,任务的切割,task的调度,还有一批Executor,Executor中会有线程池,任务会在线程池中执行。
在Spark会有一个重要的点,就是Spark Env坏境里面会有memory manager和blockManager,内存管理和块管理,这是它非常重要的,抽象地将它想成,在Spark这套应用程序中,Driver、Executor这些进程里面藏了一个HDFS存储系统,应为它的blockManager在Driver里面会有一个Master这么一个模块,在所有的Executor里面会有一个blockManager的slave,一个跟随者,一个主从的架构,比如这么一个例子,在MapReduce当中,如果有一个数据10个T,里面有男和女的记录,各占一半,要做一件事,就是只留下男的数据来统计他们的数量,和统计他们的平均年龄,怎么解决呢?我们写一个MapReduce读取这个大的文件,到map端先做过滤,把女的过滤掉只留下男的记录,要么是<男,1>,要嘛是<男,年龄>这两种格式,而且要写两个mapReduce程序,一个MapReduce是做男的统计的,另一个是做年龄的聚合的,肯定会写两个mapReduce程序,但是这两个mapReuce程序都会全量地读取10T数据,map读取的时候的数据量是很大的,但是基于这个我们可以做一个调优,什么调优呢?先写一个mapReduce程序,只有map阶段,对着10T数据进行读取,map输出的时候首先过滤掉女只剩下男的记录,这样只有5T的数据了,拿着5T的数据来跑两个MapRedce程序,这两个mapReduce程序是不用改的,则直接对着5T的数量直接统计数量和统计年龄,但是这三个作业中间是靠的是HDFS来衔接的,因为第一个通过map之后过滤掉的数据直接存到HDFS,第二个第三个也是如此的。那么将这样场景换成Spark会是怎么样的呢?Spark中先将这个10T文件利用textFile得到一个RDD,然后进行filter,过滤女的数据,filter后得到的这个RDD,然后对应不同的需求,各自取调用不同的方法区处理,一个方法得到男的统计数量,另一个方法得到年龄总和然后求平均值。那么这样会切割成两个作业,那么这样的话再加上blockManager层,因为过滤完后的rdd被复用了,被下面两个地方重复使用,那么对这个RDD做一个persist操作,
persist里面会有几个级别:
memory_only(只存内存),
memory_only_ser(内存不足使用序列化),
memory_and_disk(内存不足存磁盘),
memory_and_disk_ser(内存不足序列化,再不行就存磁盘),
这类算子,persist和cache,
叫什么算子呢?
控制算子,Spark中算子分为四类:
创建算子(textfile),转换算子(map,reducebyKey),控制算子就是(persist,checkpoint,cache),还有action算子执行算子(foreach,count),
为什么会有控制类算子呢?
一共三个,其实就是两个,因为cache=persist(memory_only),‘
’那么为什么persist中又分为内存、内存-磁盘这些级别呢?这时候就由于blockManager块存储,Spark里面就会将自己的内存空间和磁盘空间,可以做了一个几个级别的使用,如果对RDD做了memory_and_disk,如果这个数据文件有很多分区,分区中的数据,如果内存很大那么可以存到内存中,如果内存不够那么会flush到磁盘上去了,但是不管怎么样,数据的中间状态已经有了,后续的两个阶段直接基于这个中间状态数据进行计算,如果这批数据都在内存的话,计算你这批数据一定会很快。
那么这个特性叫做Spark的什么性质呢?
就是弹性了,这时候呢有的数据flush到磁盘上了,有的放到了内存中了,整个数据就不一定都是去了HDFS上了,他会让你的磁盘很快的,如果数据都在内存的话,那么计算是很快的了,所以这时候才能说成内存计算。应为这个只是那个cache算子,其他算子,只要到stage编译依然会有shuffle写的过程。
刚才说的是persist,那么checkpoint是什么意思呢?
就是将RDD数据持久化到一个外部存储系统,那么这时候可以总结了,在控制算子当中有两类算子,一个是以persist为基础的,一个是checkpoint,persist是将RDD中的数据保存到自己的进程里面,自己的进程有挂掉有丢失的风险,那么着时候可以使用checkpint,如果你的程序是很复杂很长的,到了一个中间状态,这个中间状态会做各种不同的计算,这个主干太得来不易,如果保存到自己不安全的进程中的话,那么就要把这个中间状态数据保存到一个外部存储系统当中,但是一旦启动checkpoint,就会把性能拉低到mapReduce级别性能,退化成mapRedue性能了。所以说两者是要有取舍的。