学习笔记-spark

概念

RDD: 弹性分布式数据集(由分区组成),每个分区在不同节点上
job:在里面可以看到当前应用分析出来的所有任务,以及所有的excutors中action的执行时间。

stage:在里面可以看到应用的所有stage,stage是按照宽依赖来区分的,因此粒度上要比job更细一些。从作业调度角度展示调度的阶段和任务的完成装填

task:实际的计算任务。一个task计算一个RDD的一个partition的数据。每个Executor由若干core组成,每个Executor的每个core一次只能执行一个Task。

storage:我们所做的cache persist等操作,都会在这里看到,可以看出来应用目前使用了多少缓存。从存储角度展示RDD的存储状态

environment:里面展示了当前spark所依赖的环境,比如jdk,lib等等。spark以及系统参数

executors:这里可以看到执行者申请使用的内存以及shuffle中input和output等数据。从执行角度展示每个executor运行任务的状况

Driver:spark中的driver感觉其实和yarn中Application Master的功能相类似。主要完成任务的调度以及和executor和cluster manager进行协调。有client和cluster联众模式。client模式driver在任务提交的机器上运行,而cluster模式会随机选择机器中的一台机器启动driver。从spark官网截图的一张图可以大致了解driver的功能。

https://blog.csdn.net/hjw199089/article/details/77938688 Spark中job、stage、task的划分+源码执行过程分析

https://www.jianshu.com/p/3aa52ee3a802 理解Spark中Job-Stage-Task之间的关系

Job-Stage-Task之间的关系

如下图所示,一个Spark程序可以被划分为一个或多个Job,划分的依据是RDD的Action算子,每遇到一个RDD的Action操作就生成一个新的Job。

每个spark Job在具体执行过程中因为shuffle的存在,需要将其划分为一个或多个可以并行计算的stage,划分的依据是RDD间的Dependency关系,当遇到Wide Dependency时因需要进行shuffle操作,这涉及到了不同Partition之间进行数据合并,故以此为界划分不同的Stage。

Stage是由Task组组成的并行计算,因此每个stage中可能存在多个Task,这些Task执行相同的程序逻辑,只是它们操作的数据不同。一般RDD的一个Partition对应一个Task,Task可以分为ResultTask和ShuffleMapTask。
在这里插入图片描述
https://blog.csdn.net/chengyuqiang/article/details/78170224 RDD依赖关系与stage划分

stage划分

由于shuffle依赖必须等RDD的父RDD分区数据全部可读之后才能开始计算,因此spark的设计是让父 RDD将结果写在本地,完全写完之后,通知后面的RDD。后面的RDD则首先去读之前RDD的本地数据作为输入,然后进行运算。

由于上述特性,将shuffle依赖就必须分为两个阶段(stage)去做:

(1)第1个阶段(stage)需要把结果shuffle到本地,例如reduceByKey,首先要聚合某个key的所有记录,才能进行下一步的reduce计算,这个汇聚的过程就是shuffle。

(2)第2个阶段(stage)则读入数据进行处理。

为什么要写在本地?

后面的RDD多个分区都要去读这个信息,如果放到内存,如果出现数据丢失,后面的所有步骤全部不能进行,违背了之前所说的需要父RDD分区数据全部ready的原则。

同一个stage里面的task是可以并发执行的,下一个stage要等前一个stage ready(和mapreduce的reduce需要等map过程ready 一脉相承)。

Spark 将任务以 shuffle 依赖(宽依赖)为边界打散,划分多个 Stage. 最后的结果阶段叫做 ResultStage, 其它阶段叫 ShuffleMapStage, 从后往前推导,依将计算。

在这里插入图片描述
1.从后往前推理,遇到宽依赖就断开,遇到窄依赖就把当前RDD加入到该Stage

2.每个Stage里面Task的数量是由该Stage中最后一个RDD的Partition的数量所决定的。

3.最后一个Stage里面的任务类型是ResultTask,前面其他所有的Stage的任务类型是ShuffleMapTask。

4.代表当前Stage的算子一定是该Stage的最后一个计算步骤

表面上看是数据在流动,实质上是算子在流动。

(1)数据不动代码动

(2)在一个Stage内部算子为何会流动(Pipeline)?首先是算子合并,也就是所谓的函数式编程的执行的时候最终进行函数的展开从而把一个Stage内部的多个算子合并成为一个大算子(其内部包含了当前Stage中所有算子对数据的计算逻辑);其次,是由于Transformation操作的Lazy特性!在具体算子交给集群的Executor计算之前首先会通过Spark Framework(DAGScheduler)进行算子的优化(基于数据本地性的Pipeline)。

Spark SQL job stage 切分规则
读一张表会切分一个job

sort merge join 会切分一个job

job中 遇到 shuffle(group by,distinct,distribute by,join) 会切分 stage,这个和rdd的切分一样

stage 中 连着的 filter,project 操作可以形成一个 whole stage codegen

Spark SQL特点

1.容易集成:已经集成到 Spark 中
2.提供统一的数据访问方式:访问接口 DataFrame(DataSet)
Spark SQL数据源:Oracle中的数据、文本(csv、json)、Hive 的表
3.兼容 Hive
4.提供标准的数据连接方式:JDBC 和 ODBC

Spark SQL 的数据模型:DataFrame(表)

1.表=表结构 + 数据
2.DataFrame = Schema(表结构) + RDD(数据)

https://snaildove.github.io/2020/02/10/summary_of_Translation(SparkTheDefinitiveGuide)_online/ 《Spark权威指南》的翻译综述

Transformations:
一个RDD上指定转换以创建另一个
distinct、filter、map、flatMap、sort

Actions:
action(动作/算子)要么将数据收集到驱动程序,要么写入外部数据源。
reduce、count、first、max、take、saveAsTextFile

共享变量:
1.广播变量是共享的,不可变的变量,它们缓存在集群中的每台计算机上,而不是与每个任务序列化。
2.累加器是Spark的第二种共享变量,是一种在各种转换中更新值并将该值以有效且容错的方式传播到驱动程序节点的方法。

几个概念之间的关系

梳理一下Spark中关于并发度涉及的几个概念File,Block,Split,Task,Partition,RDD以及节点数、Executor数、core数目的关系。
在这里插入图片描述
输入可能以多个文件的形式存储在HDFS上,每个File都包含了很多块,称为Block。
当Spark读取这些文件作为输入时,会根据具体数据格式对应的InputFormat进行解析,一般是将若干个Block合并成一个输入分片,称为InputSplit,注意InputSplit不能跨越文件。
随后将为这些输入分片生成具体的Task。InputSplit与Task是一一对应的关系。
随后这些具体的Task每个都会被分配到集群上的某个节点的某个Executor去执行。

每个节点可以起一个或多个Executor。
每个Executor由若干core组成,每个Executor的每个core一次只能执行一个Task。
每个Task执行的结果就是生成了目标RDD的一个partiton。
注意: 这里的core是虚拟的core而不是机器的物理CPU核,可以理解为就是Executor的一个工作线程。

而 Task被执行的并发度 = Executor数目 * 每个Executor核数。

至于partition的数目:

  • 对于数据读入阶段,例如sc.textFile,输入文件被划分为多少InputSplit就会需要多少初始Task。
  • 在Map阶段partition数目保持不变。
  • 在Reduce阶段,RDD的聚合会触发shuffle操作,聚合后的RDD的partition数目跟具体操作有关,例如repartition操作会聚合成指定分区数,还有一些算子是可配置的。

spark中partition与block的关系

hdfs中的block是分布式存储的最小单元,类似于盛放文件的盒子,一个文件可能要占多个盒子,但一个盒子里的内容只可能来自同一份文件。假设block设置为128M,你的文件是250M,那么这份文件占3个block(128+128+2)。这样的设计虽然会有一部分磁盘空间的浪费,但是整齐的block大小,便于快速找到、读取对应的内容。(p.s. 考虑到hdfs冗余设计,默认三份拷贝,实际上3*3=9个block的物理空间。)
spark中的partition 是弹性分布式数据集RDD的最小单元,RDD是由分布在各个节点上的partition 组成的。partition 是指的spark在计算过程中,生成的数据在计算空间内最小单元,同一份数据(RDD)的partition 大小不一,数量不定,是根据application里的算子和最初读入的数据分块数量决定的,这也是为什么叫“弹性分布式”数据集的原因之一。
总结:

  • block位于存储空间、partition 位于计算空间,
  • block的大小是固定的、partition 大小是不固定的,
  • block是有冗余的、不会轻易丢失,partition(RDD)没有冗余设计、丢失之后重新计算得到.

spark 和 mr 的区别

  • 最大的区别: 中间数据的存储/落地。 Spark一针见血的解决了Hadoop MR的痼疾:神马东西/屁大点儿东西都要往HDFS上面放,HDFS是跨机器的,多副本的,你这一遍一遍的写,然后一遍一遍的拉取,天都黑了;而Spark针对这个问题,能不落地的就不落地,非要落地的也可以直接落地到本地磁盘上,而非总是通过网络(HDFS)读写,这节省出来的latency时间5分钟,可以通话两小时!Hadoop认为数据总是很大的,节点总是会挂的,所以它推崇一步一个脚印的搞,所以就慢的令人发指;而众所周知,数据集在很多时候是可以想办法变小的, Spark就抓住这一点,你的数据集并非总是那么大,你的机器也并非总是会挂,哥就跑个完美的场景: 数据集小于集群内存,生存的文件经常都很小,这时候就是Spark开挂的时候了! 在今天很多2U集群动辄双路/四路CPU,128GB内存,12TB硬盘的情况下,稍微多几个节点,数据很”大“的问题就没有那么严重了。这点在最早期版本的Spark中尤为明显,数据几乎都不落地,所以性能碾压Hadoop惨绝人寰,当然致命的问题就是, RDD经过各种转换之后,经常爆仓了, OOM,然后。。。然后现在也map都要每一条落地了,通过配置,也可以合并小文件了,性能嘛,在大数据集下也就下降了,这就是上面讲的”趋同化“导致的,所以如果有人直接告诉你Spark百倍于Hadoop,那是耍流氓。

  • Hadoop的reducer一定要等到所有的mapper全部完成之后才能开始,而Spark的reducer在mapper的总算完成到一定的量之后就会开始,也就是可以一边map一边reduce,这估计是另外一个加速度的效果导致比Hadoop的MR要快。 一边map一边reduce 存疑,有的地方认为是 Spark的Shuffle是边拉取数据边进行Aggregate操作的

  • Spark具体到代码级别的实现时,有很多惰性求值的写法在里面,导致程序运行的效率在看不见的地方却比Hadoop优化了非常多,怎么类比好呢?Hadoop的MR方式具体在实现上就好比我们用同步的命令式的Javascript方式实现了数据库连接,而Spark采用了异步的闭包式的搞法来连接,威力是很不一样的;这些特征在Shuffle这类操作是功效尤为明显;但是在Java 8发布之后,以及将来Java继续扩展FP式的功能之后, Spark的这方面的优势可能就没有那么明显了。

  • Spark讲Inputs抽象称为RDD,在各个不同的Stage,各种花式转换,之后还是RDD,相对于Hadoop那一坨一坨的MR简直不要优雅,所以如果你想定制Shuffle阶段的比如排序算法,都会更加越来越熟练,而Hadoop的MR写完一周没看,下一次拿起来完全不知道什么东西; RDD这个抽象我觉得在代码的可读性上面做的简直太好了,看到RDD时有当年看到Wordpress将所有内容抽象成为post时的会心一笑,这对于学习一个开源的框架实在太重要了,我依然觉得,抽象的越优雅的东西越简单,越简单的东西越容易组合,组合的力量也越大。

  • Hadoop的MR过程,如果一个Job执行错了,需要从头再来一遍;而Spark的job执行有类似于“增量”的效果在里面, 如果job执行到哪个步骤出错了,这个错误之前的结果会被直接用到下一次的job执行中,也即是job可以做到从出错的地方开始,而非每次都全部从头再来。

MapReduce的主要限制之一是在运行每个作业后,它会将整个数据集保存到HDFS中。这是非常昂贵的,因为它(在复制中)会导致磁盘I / O中的数据集大小和网络I / O数量相近的三倍(用于复制)。Spark对操作流程采取了更为全面的看法。当一个操作的输出需要馈入另一个操作时,Spark直接传递数据而无需写入持久性存储。这是来自Microsoft Dryad论文的MapReduce的一项创新,并非Spark独创。

Spark的主要创新是引入了内存中的缓存抽象。这使得Spark非常适合多个操作访问相同输入数据的工作负载。用户可以指示Spark将输入数据集缓存在内存中,因此无需为每次操作都从磁盘读取它们。

那将归结为一个MapReduce作业的Spark作业又如何呢?在许多情况下,它们在Spark上的运行速度也比在MapReduce上快。Spark在这里的主要优点是它可以更快地启动任务。MapReduce为每个任务启动一个新的JVM,加载JAR,JITing,解析配置XML等可能需要几秒钟。Spark使执行者JVM在每个节点上运行,因此启动任务仅是对其执行RPC的问题,并且将Runnable传递给线程池,该线程池以毫秒为单位。

最后,一个可能值得一提的常见误解是,Spark某种程度上完全在内存中运行,而MapReduce却没有。这根本不是那么回事。Spark的shuffle实现与MapReduce的实现非常相似:将每个记录序列化并写到map端的磁盘上,然后在reduce端进行提取和反序列化。

Spark使用“惰性评估”来形成连续计算阶段的有向无环图(DAG)。以此方式,可以优化执行计划,例如以最小化周围的改组数据。相反,这应该在MapReduce中通过调整每个MR步骤手动完成。

github学习:https://github.com/JerryLead/SparkInternals/tree/master/markdown

内存溢出

内存溢出无非两点:

  1. Driver 内存不够

  2. Executor 内存不够

Driver 内存不够无非两点:
  1. 读取数据太大
    解决思路是增加 Driver 内存,具体做法为设置参数
  2. 数据回传,大量数据回传 Driver,造成内存溢出
    解决思路是分区输出,具体做法是 foreach
Executor 内存不够无非两点:
  1. map 类操作产生大量数据,包括 map、flatMap、filter、mapPartitions 等
    解决思路是减少每个 task 的大小,从而减少每个 task 的输出;
    具体做法是在 会产生大量对象的 map 操作前 添加 repartition(重新分区) 方法,分区成更小的块传入 map
  2. shuffle 后产生数据倾斜

https://www.kancloud.cn/kancloud/spark-internals/45240 Apache spark 设计与实现

Dataframe 性能高于 RDD: https://www.jianshu.com/p/9e6482bcec99 Spark–Spark RDD与Dataframe/Datasets对比学习
https://juejin.im/post/5eda641cf265da76ee1f58e6 大数据研发工程师(两年)字节跳动面经
https://dongkelun.com/2018/06/03/sparkCacheAndPersist/ Spark 持久化(cache和persist的区别)
https://blog.csdn.net/xin93/article/details/80697488 【Spark】Spark性能优化之Whole-stage code generation 避免使用Volcano模型,在运行时动态生成代码。

UI:https://spark.apache.org/docs/3.0.0-preview/web-ui.html

  • WholeStageCodegen: Whole-Stage Code Generation (aka WholeStageCodegen or WholeStageCodegenExec) fuses multiple operators (as a subtree of plans that support codegen) together into a single Java function that is aimed at improving execution performance. It collapses a query into a single optimized function that eliminates virtual function calls and leverages CPU registers for intermediate data.将多个operators合并成一个java函数,从而提高执行速度
  • HiveTableScan:扫描hive表
  • HashAggregate:基于Hash Map 的聚合实现,如sum,count
  • Project:投影/只取所需列
  • Exchange:stage间隔,产生了shuffle
  • Filter:过滤(如果筛选字段为分区,不属于Filter,属于HiveTableScan)
  • CollectLimit:limit 数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值