Spark是一个快速的大数据处理引擎,在实际的生产环境中,应用十分广泛。目前,Spark仍然是大数据开发非常重要的一个工具,所以在面试的过程中,Spark也会是被重点考察的对象。对于初学者而言,面对繁多的Spark相关概念,一时会难以厘清头绪,对于使用Spark开发的同学而言,有时候也会对这些概念感到模糊。本文主要梳理了几个关于Spark的比较重要的几个概念,在面试的过程中如果被问到Spark相关的问题,具体可以从以下几个方面展开即可,希望对你有所帮助。本文主要包括以下内容:
组成
Spark栈包括SQL和DataFrames,MLlib机器学习,GraphX和SparkStreaming。用户可以在同一个应用程序中无缝组合使用这些库。
架构
Spark运行架构包括集群资源管理器(Cluster Manager)、运行作业任务的工作节点(WorkerNode)、每个应用的任务控制节点(Driver)和每个工作节点上负责具体任务的执行进程(Executor)。其中,集群资源管理器可以是Spark自带的资源管理器,也可以是YARN或Mesos等资源管理框架。
运行流程
MapReduce VS Spark
与Spark相比,MapReduce具有以下缺点:
与MapReduce相比,Spark具有以下优点:具体包括两个方面
驱动程序(Driver)和Executor
运行main函数的驱动程序进程位于集群中的一个节点上,负责三件事:
驱动程序进程是绝对必要的——它是 Spark 应用程序的核心,并在应用程序的生命周期内维护所有相关信息。
Executor负责实际执行驱动程序分配给他们的任务。这意味着每个Executor只负责两件事:
分区
为了让每个 executor 并行执行工作,Spark 将数据分解成称为partitions的块。分区是位于集群中一台物理机器上的行的集合。Dataframe 的分区表示数据在执行期间如何在机器集群中物理分布。
如果你有一个分区,即使你有数千个Executor,Spark 的并行度也只有一个。如果你有很多分区但只有一个执行器,Spark仍然只有一个并行度,因为只有一个计算资源。
执行模式:Client VS Cluster VS Local
执行模式能够在运行应用程序时确定Driver和Executor的物理位置。
有三种模式可供选择:
集群模式 可能是运行 Spark应用程序最常见的方式。在集群模式下,用户将预编译的代码提交给集群管理器。除了启动Executor之外,集群管理器会在集群内的工作节点(work)上启动驱动程序(Driver)进程。这意味着集群管理器负责管理与Spark 应用程序相关的所有进程。
客户端模式 与集群模式几乎相同,只是 Spark 驱动程序保留在提交应用程序的客户端节点上。这意味着客户端机器负责维护 Spark driver进程,集群管理器维护 executor 进程。通常将这个节点称之为网关节点。
本地模式可以被认为是在你的计算机上运行一个程序,spark 会在同一个 JVM 中运行驱动程序和执行程序。
RDD VS>
如上图,首先Driver将序列化对象分割成小的数据库,然后将这些数据块存储在Driver节点的BlockManager上。当ececutor中执行具体的task时,每个executor首先尝试从自己所在节点的BlockManager提取数据,如果之前已经提取的该广播变量的值,就直接使用它。如果没有找到,则会向远程的Driver或者其他的Executor中提取广播变量的值,一旦获取该值,就将其存储在自己节点的BlockManager中。这种机制可以避免Driver端向多个executor发送数据而造成的性能瓶颈。
累加器
累加器(Accumulator)是Spark提供的另外一个共享变量,与广播变量不同,累加器是可以被修改的,是可变的。每个transformation会将修改的累加器值传输到Driver节点,累加器可以实现一个累加的功能,类似于一个计数器。Spark本身支持数字类型的累加器,用户也可以自定义累加器的类型。
宽依赖和窄依赖
RDD中不同的操作会使得不同RDD中的分区产不同的依赖,主要有两种依赖:宽依赖和窄依赖。宽依赖是指一个父RDD的一个分区对应一个子RDD的多个分区,窄依赖是指一个父RDD的分区对应与一个子RDD的分区,或者多个父RDD的分区对应一个子RDD分区。
窄依赖会被划分到同一个stage中,这样可以以管道的形式迭代执行。宽依赖所依赖的分区一般有多个,所以需要跨节点传输数据。从容灾方面看,两种依赖的计算结果恢复的方式是不同的,窄依赖只需要恢复父RDD丢失的分区即可,而宽依赖则需要考虑恢复所有父RDD丢失的分区。
DAGScheduler会将Job的RDD划分到不同的stage中,并构建一个stage的依赖关系,即DAG。这样划分的目的是既可以保障没有依赖关系的stage可以并行执行,又可以保证存在依赖关系的stage顺序执行。stage主要分为两种类型,一种是ShuffleMapStage,另一种是ResultStage。其中ShuffleMapStage是属于上游的stage,而ResulStage属于最下游的stage,这意味着上游的stage先执行,最后执行ResultStage。
持久化
方式
在Spark中,RDD采用惰性求值的机制,每次遇到action操作,都会从头开始执行计算。每次调用action操作,都会触发一次从头开始的计算。对于需要被重复使用的RDD,spark支持对其进行持久化,通过调用persist()或者cache()方法即可实现RDD的持计划。通过持久化机制可以避免重复计算带来的开销。值得注意的是,当调用持久化的方法时,只是对该RDD标记为了持久化,需要等到第一次执行action操作之后,才会把计算结果进行持久化。持久化后的RDD将会被保留在计算节点的内存中被后面的行动操作重复使用。
Spark提供的两个持久化方法的主要区别是:cache()方法默认使用的是内存级别,其底层调用的是persist()方法。
持久化级别的选择
Spark提供的持久化存储级别是在内存使用与CPU效率之间做权衡,通常推荐下面的选择方式:
tips:在一些shuffle算子中,比如reduceByKey,即便没有显性调用persist方法,Spark也会自动将中间结果进行持久化,这样做的目的是避免在shuffle期间发生故障而造成重新计算整个输入。即便如此,还是推荐对需要被重复使用的RDD进行持久化处理。
coalesce VS repartition
repartition算法对数据进行了shuffle操作,并创建了大小相等的数据分区。coalesce操作合并现有分区以避免shuffle,除此之外coalesce操作仅能用于减少分区,不能用于增加分区。
值得注意的是:使用coalesce在减少分区时,并没有对所有数据进行了移动,仅仅是在原来分区的基础之上进行了合并而已,所以效率较高,但是可能会引起数据倾斜。
综合案例
一种数仓技术架构
SparkStreaming实时同步
SparkStreaming消费kafka埋点数据
将解析的数据同时写入HDFS上的某个临时目录下及Hive表对应的分区目录下
由于是实时数据抽取,所以会生成大量的小文件,小文件的生成取决于SparkStreaming的BatchInterval,比如一分钟一个batch,那么一分钟就会生成一个小文件
基于SparkSQL的批处理
SparkStreaming数据输出是At LeastOnce,可能会存在数据重复。在ODS层到DWD层进行明细数据处理时,需要对数据使用row_number去重。
数据量大时,需要对数据进行重分区,并且为DataSet分区级别建立连接,采用批量提交的方式。
spark.sql.shuffle.partitions的默认值为200,会导致以下问题
使用 DISTRIBUTE BY cast( rand * N as int) 这里的N是指具体最后落地生成多少个文件数。
手动维护offset至HBase
当作业发生故障或重启时,要保障从当前的消费位点去处理数据,单纯的依靠SparkStreaming本身的机制是不太理想,生产环境中通常借助手动管理来维护kafka的offset。
流应用监控告警
本网站的文章部分内容可能来源于网络和网友发布,仅供大家学习与参考,如有侵权,请联系站长进行删除处理,不代表本网站立场,转载者并注明出处:https://www.jmbhsh.com/shipinzhuangshi/36497.html