spark在很多公司中都有线上应用,多是用在处理数据上面,语法相较于hadoop更加简单,而且更易理解,集群也更易管理,但是还是有很多技巧可寻,掌握这些技巧对提升工作效率来说非常重要

总述:本文主讲的是client模式下的spark使用经验

1:本文主讲的是client模式下的spark使用经验,spark由driver和executor组成,比如说1个driver和3个executor,driver提交节点提交节点资源,由driver-memory和drive程序组成,YARN资源队列有几个重要的名词,num_executors/executor-cores/executor-memory这几个。

其中client模式下,集群yarn无法控制driver上cores的使用数目和占用率,但是driver-memory是由java jvm组成的,不会超量

2:举一个实际案列,假如有一个user提交1个spark程序,长时间无法获得资源开始执行,并且假设YARN的资源队列没有满

那么这时候应该去看看他的spark-shell或者spark-submit的启动参数,一般情况下都是他executor-cores设置的过大了,>2一般就判断为过大

这块的原因是:在申请资源的时候,spark集群上的机器必须只有当 当台机器上的剩余cores>10,才能给用户来进行申请,所以越大一般就会越慢,这个user已经到10了,所以就非常大了

3:接下来讲一讲spark的执行原理

假如你是一段sql的话,读取了一个表的3个分区,每个分区的part数分别是4 5 6,那么你读到的rdd的partition数就是4+5+6=15,也就是说hdfs文件有多少个分区,你的rdd初始也就是多少个partition数。

在scala中,map filter flatMap这三个操作属于不会改变rdd分区数目的操作,reduceByKey join repartition的操作属于shuffle操作,会改变rdd的分区数,其中join操作大家可能不理解,熟悉python的人会以为join操作是在进行groupByKey,但实际上reduceByKey会比groupByKey块很多,所以scala中这个函数接口使用reduceByKey也写,有兴趣的朋友也可以自己实现下,不到20行就成。言归正传,改变分区数的时候,默认会改成200,这几个shuffle操作的函数也可以覆盖默认的分区数。除了这个默认参数,还可以在启动spark的时候,--conf中用参数进行默认值覆盖

当启动了3个executor的时候,也就是启动了3个物理机,假如一个物理机有2个core 也就是你申请了2个core,你读到的rdd有6个分区,所以正常情况下会一个executor处理2个分区,

在经历shuffle类的操作的时候,你的整个spark程序就会运行到一个新的stage(相比于之前那个stage,增大1)

4:spark的执行原理--系列2--shuffle的原理

shuffle操作的时候,实际上都要经历一个排序过程,在task内部进行外部排序,排序的时候会使用key来进行排序,排完序后也会把key相同的数据方法哦一个分区里面,所以很容易出现数据倾斜的问题(shuffle write过程)

多个task做完排序后,接下来是reducer的操作,经过ExternalOnlyAppendMap操作,将相同的key的数据放到同一个block中(shuffle read过程)

网上很多资料有讲到一个RDD的executor操作如果同时处理两个分区数据的时候,这两个分区(stage)会共同占用这个executor的资源,但实际上我启动这样一个任务后去spark中看过资源情况,如果一个executor的内存设置为2G,2个core,那么这个executor就会占用到4G的内存

5:spark的执行原理--系列2--统一内存管理

spark中有3部分内存,

user memory:这里面存放的是java中的变量、类等

storage memory: RDD cache后、用户的自定义广播变量、系统的广播变量

execution memory: 程序进行shuffle、非shuffle操作的时候,都会被占用

但是storage memory execution memory之间的界限不明显,会在程序实际运行中互相占用

假设定义RDD&shuffle可用内存设置为0.6  spark.memory.fraction=0.6,将可用内存分成两部分的操作时 设置spark.memory.storageFraction=0.5,既然设定了storage用0.5,那么execute就用0.1,但实际上会相互调用

6:作业性能调优--这个章节主要讲解一些使用经验

第一个stage的task数目就是读取HDFS文件生成RDD的分区数

若不经过shuffle,RDD的分区数不会变化(union后是分区数之和,整体不变)

shuffle后分区数默认变成200

在stage内,每个分区对应一个task,是否所有的分区会同时执行,这取决于什么?取决于每个executor中的core数目

什么时候有必要改写RDD的分区数?多少合适些?当进行join这样的操作时,数据会极度的加大,这时候需要进行分区;每个分区的数据量大小需要控制到100MB到2G之间,不能太大,不能太小,样本数需要在50万左右即可;假如你申请的一个executor的内存是2G,那么一个分区的数据量大小最好在2G的一半左右;可以多尝试不同的分区数来尝试,因为你的程序可能不止是一遍跑完就算了

RDD的分区数=block数

前面的几种shuffle操作,比如join reduceByKey repartition的默认分区数是200,这几种操作都有个设置分区数的参数,可以自动修改。也可以在启动spark-shell或者spark-submit的时候,指定 --conf spark.default.parallrlism=1000 \ 来进行覆盖

假如1000个分区、100个executor、每个executor只有1个core,那么刚开始就会执行100个分区的数据;假如每个executor只有2个core ,那么刚开始就会执行200个分区的数据,而且每个executor 的2个core的占用内存并不是公用这个executor的内存,而是每个core都分配到你设置的executor的内存(这个理论和大多数不一样,但是可以去sparkui中实际计算下

7:接下来将几个shuffle调优的经验

假如有个任务在spark1.6中可以照常运行,在spark2.1中不可以,输入的数据量是6T。2.1中报错中有这样几个明显的标志--java.lang.OutOfMemory + ExternalAppendOnlyMap.insertAll  CoGroupedRDD.comute这几个错误,那么可以断定是在shuffle截断报的错。

又因为是版本的不同,所以可以断定是--conf spark.reducer.maxReqsInFlight=10 这个参数的问题

maxReqsInFlight是指定的在shuffle操作时,执行节点向shuffle后的数据去拉取key的并行度,spark1.6中有限制数目,spark2后这个参数是无限大,在这个场景下,数据量过大,key数目就过大,拉取的并行度过大的时候就会出现内存错误。。
解决办法是:将这个参数设置成10

8:接下来讲解一个提交节点的错误,简单点直接说排查到的错误吧

当driver上的cpu使用率达到最大负载的时候,即使集群还有资源,你也提交不上去任务

9:  如果遇到自己的spark任务,本身有8个stage,但是查看spark ui,看到卡在第3个stage好久(大于1个小时),原因是 内存报错了,记得加大exe和提交节点的内存

10:一个开发经验很丰富的同事告诉我他的调优经验,就三句话

partition不要太大官方建议50~200m, cache要么mem only 要么 disk only; broadcast不超过2g

数据倾斜靠采样找到倾斜的key 然后 倾斜key出来单独处理

11:能用reduceByKey尽量用它

spark yarn 队列_spark