Spark调优

Spark调优:
总共分为四点:
1.开发调优 2.资源调优 3.数据倾斜 4.shuffle

1.开发调优
1)避免创建重复的RDD(不包含数据,抽象描述)
如果是需要对一个文件进行多次计算,那么注意,最好就只读一次。RDD:不可变可分区的弹性分布式数据集。

2)尽可能复用同一个RDD
3)对多次使用的RDD进行持久化(cache persist)(内存或磁盘)
rdd1.map.reduce.sort.map()
rdd1.map.reduce.groupByKey()
改进:
rdd2.cache
rdd2.sort.map()
rdd2.groupByKey()
程序运行过程中的数据放置在内存。如果程序执行完毕,那么肯定中间产生的数据会被垃圾回收。
4)尽量避免使用shuffle类算子
shuffle的操作有一个特点:
上一个阶段必须全部执行完毕之后,下一个阶段才能执行
任务不同步(有的任务运行的时间快,有的慢)
分布式计算,决定了一定会有shuffle
shuffle算子: reduceByKey,groupBy,sortBy,distinct,
集合类的操作基本都有shuffle算子
在mapreduce当中,我们知道如果定义mapreduce的map join实现程序
在spark中,如何实现?
BroadCast 使用广播变量
rdd1.join(rdd2)
 

val bc=sparkContext.broadCast(rdd.tolist)
 rdd1.foreachPartition(data =>{
   val data2=bc.value  //小表数据
     val data1=data     //大表数据的其中一个分区
   data1.join(data2)  //这句代码,是在每个节点上独立运行的
})

5)使用map-side预聚合的shuffle操作
shuffle类的算子有第三分缺点:数据倾斜
目的:在使用shuffle操作的算子的时候,如果有map-side预聚合的话,那么shuffle的代价还是很小的
哪些shuffle算子没有map-side:
groupByKey 有shuffle 没有聚合
coGroup :只做分组,不做聚合
再看看有聚合的shuffle算子:
执行流程上: reduceByKey =groupByKey+reduce
最终的效率上 reduceByKey> groupByKey +reduce
reduceBuyKey ,combineByKey,aggregateByKey

6)使用高性能的算子

使用reduceByKey/aggregateByKey替代groupByKey
2.使用mapPartitions替代普通map(粒度越大越好)
原理:如果一个操作能针对partition完成,就不要针对单个元素。
3.使用foreachPartitions替代foreach
4.使用filter之后进行coalesce(重新分区)操作
5.使用repartitionAndSortWithinPartitions替代repartition与sort类操作
7)广播大变量
目的:让多个task都要使用的在driver中声明的变量都要维持一个独立副本,编程让这个executor中所有task都公用一个副本
效果;executor的内存占用量就减少了,网络传输少了
原则:要广播的数据越大,进行广播这个操作之后得到的收益越好,如果executor的并发度越高,其实执行广播操作,收益越好。

8)使用Kryo优化序列化性能
序列化:简单的说,就是把一个对象变成一个010101数组
反序列化:把0101010这种格式的数还原成一个对象
java 中创建对象的方式:

java:实现序列化;
让参与序列化的类型implements Serializable
有个缺点:
除了把当前这个对象的属性的值给存储/携带之外
还会把当前这个对象的类型的信息都携带
mapreduce:
序列化:自定义了规则:
对于类型信息,只会携带一次
spark的序列化:
1.默认情况下,是支持java的原生序列化的机制
2.使用kyriserializer

9)优化数据结构
java中,有三种类型比较耗费内存:
字符串
对象
集合
优化措施:
使用原始类型(Int,Long)(基本类型)替代字符串String char 一串字符 char[]
尽量使用字符串替代对象
使用数组代替集合

10)融汇贯通

2.资源调优
1.概述:
在开发完Spark作业之后,就该为作业配置合适的资源了,Spark的资源参数基本都可以在spark-submit命令中作为参数设置。因此,我们必须对Spark中的资源使用原理有一个清晰的认识,并知道,有哪些资源参数可以设置的,以及如何设置合适的参数值。
2.Spark作业基本运行原理

Executor的内存主要分为三块:
第一块是让task执行我们自己编写的代码时使用,默认是占Executor总内存的20%;
第二块是让task通过shuffle过程拉取了上一个stage的task的输出后,进行聚合等操作时使用,默认也是占Executor总内存的20%;
第三块是让RDD持久化时使用,默认占Executor总内存的60%。
3.资源参数调优
1)num-executor
说明:该参数用于设置Spark作业总共要用多少个Executor进行来执行。这个参数非常重要,如果不设置的话,默认只会给你启动少量的Executor进程,此时你的spark作业运行的速度非常慢。
建议:根据任务大小,和集群大小取设置。
2)executor-memeory
说明:该参数用于设置每个Executor进程的内存。
建议:,具体的设置还是得根据不同部门的资源队列来定。可以看看自己团队的资源队列的最大内存限制是多少,num-executors乘以executor-memory,是不能超过队列的最大内存量的。此外,如果你是跟团队里其他人共享这个资源队列,那么申请的内存量最好不要超过资源队列最大总内存的1/3~1/2,避免你自己的Spark作业占用了队列所有的资源,导致别的同学的作业无法运行。
3)executor-cores
说明:该参数用于设置每个Executor进程的CPU core数量,这个参数决定了每个Executor进程并行执行task线程的能力。
建议:如果是跟他人共享这个队列,那么num-executors * executor-cores不要超过队列总CPU core的1/3~1/2左右比较合适,也是避免影响其他同学的作业运行。
4)dirver-memory
说明:该参数用于设置Driver进程的内存
建议:不设置或设置1G左右应该就够了。注意:如果需要使用collect算子将RDD的数据全部拉取到Driver上进行处理,那么必须确保Driver的内存足够大,否则会出现OOM内存溢出的问题。
5)spark.default.parallelism
说明:该参数用于设置每个stage的默认task数量,这个参数极为重要,如果不设置可能直接影响你的Spark作业性能。
建议:该参数设置为num-executors*executor-cores的2-3倍较为合适。
6)spark.storage.memeoryFraction
说明:该参数用于设置RDD持久化数据在Executor内存中能占的比列,默认是0.6。
建议:如果Spark作业中,有较多的RDD持久化操作,该参数的值可以适当提高一些,保证持久化的数据能够容纳在内存中。避免内存不够缓存所有的数据,导致数据只能写入磁盘中,降低了性能
7)spark.shuffle.memeoryFraction
说明:该参数用于设置shuffle过程中一个task拉取上个stage的输出后,进行聚合操作时能够使用的Executor内存的比例,默认是0.2。
建议:如果Spark作业中的RDD持久化操作较少,shuffle操作较多时,建议降低持久化操作的内存占比,提高shuffle操作的内存占比比例

3.数据倾斜调优
1.概述:
数据倾斜是大数据技术中进程遇到的问题,会影响spark作业的性能。数据倾斜调优,就是使用各种技术方案解决不同类型的数据倾斜问题,以保证Spark作业的性能。

2.数据倾斜发生时的现象
1).有的task执行的很快,有的task执行的很慢,
2).原本正常执行的spark作业,某天突然报出OOM(内存溢出)异常,观察异常栈,是我门写业务代码造成的。

3.数据倾斜发生的原理
在进行shuffle的时候,必须将各个节点上相同的key拉取到某个节点上的一个task来进行处理,比如按照key进行聚合或join等操作。此时如果某个key对应的数据量特别大的话,就会发生数据倾斜。

4.如何定位导出数据倾斜的代码
数据倾斜只会发生在shuffle过程中。
可能会触发shuffle操作的算子:distinct、groupByKey、aggreateByKey、join、cogroup、repartition。
1)某个task执行特别慢的情况
首先要看的,就是数据倾斜发生在第几个stage中。
如果是用yarn-client模式提交,那么本地是直接可以看到log的,可以在log中找到当前运行到了第几个stage;如果是用yarn-cluster模式提交,则可以通过Spark Web UI来查看当前运行到了第几个stage。此外,无论是使用yarn-client模式还是yarn-cluster模式,我们都可以在Spark Web UI上深入看一下当前这个stage各个task分配的数据量,从而进一步确定是不是task分配的数据不均匀导致了数据倾斜。
2)某个task莫名其妙内存溢出的情况
这种情况下去定位出问题的代码就比较容易了。我们建议直接看yarn-client模式下本地log的异常栈,或者是通过YARN查看yarn-cluster模式下的log中的异常栈。一般来说,通过异常栈信息就可以定位到你的代码中哪一行发生了内存溢出。然后在那行代码附近找找,一般也会有shuffle类算子,此时很可能就是这个算子导致了数据倾斜。

5.查看导致数据倾斜的key的数据分布情况
目的:查看一下其中key的分布情况。这主要是为之后选择哪一种技术方案提供依据。
1)如果是Spark SQL中的group by、join语句导致的数据倾斜,那么就查询一个SQL使用的表的Key分布情况。
2)如果对Spark RDD执行的shuffle算子导致的数据倾斜,那么 可以在Spark作业中加入查看key分布的代码,比如RDD.countByKey()。然后统计出来的各个key出现的次数,collect/take到客户端打印一下。

6.解决方案
解决方案一:使用Hive ETL预处理数据
1)适用场景:导致数据倾斜的是Hive表。
如果该Hive表中的数据本身很不均匀,而且业务场景需要频繁使用Spark对Hive表执行某个分析操作。
2)实现原理
Hive ETL中进行group by或者join等shuffle操作时,还是会出现数据倾斜,导致Hive ETL的速度很慢。我们只是把数据倾斜的发生提前到了Hive ETL中,避免Spark程序发生数据倾斜而已。
3)实践经验
在Java系统与Spark结合使用的项目中,会出现Java代码频繁调用Spark作业的场景,而且Spark作业的执行性能要求很高。
方法是:将数据倾斜提前提到上游的Hive ETL,每天仅执行一次,只是那一次比较慢的,而之后每次Java调用Spark作业时,执行数据很快,能够提供更好的用户体验。

解决方案二、过滤少数导致数据倾斜的Key
1)使用场景
如果发现导致数据倾斜的key就少数几个,而且对计算本身的影响并不大的话,那就很适合使用这种方案。
2)实现思路
直接过滤掉那少数几个key。比如,在Spark SQL中可以使用where子句过滤掉这些key或者在Spark Core中对RDD执行filter算子过滤掉这些key。
3)实际经验
Hive表中的某一个key在那天数据异常,导致数据量暴增。因此就采取每次执行前先进行采样,计算出样本中数据量最大的几个key之后,直接在程序中将那些key给过滤掉。

解决方案三、提高shuffle操作的并行度
1)适用场景
这是处理数据倾斜最简单的一种方案
2)实现思路
在对RDD执行shuffle算子时,给shuffle算子传入一个参数,比如reduceByKey(1000),该参数就设置;1这个shuffle算子在执行是shuffle read task的数量。
3)实践经验
在发现数据倾斜时尝试使用的第一种手段,尝试去用最简单的方法缓解数据倾斜而已,或者是和其他方案结合起来使用。

解决方案四:两阶段聚合(局部聚合+全局聚合)
1)适用场景
对RDD执行reduceByKey等聚合类shuffle算子或者在Spark SQL中使用group by语句进行分组聚合时,比较适用这种方案。
2)实现思路
第一是局部聚合,先给每个 key打上一个随机数,接着对打上随机数的数据执行reduceByKey等聚合操作,进行局部聚合,再把各个key的前缀去掉,再次进行全局聚合。
3)优缺点
对于聚合类的shuffle操作导致的数据倾斜,效果是非常不错的。通常都可以解决掉数据倾斜,或者至少是大幅度缓解数据倾斜,将Spark作业的性能提升数倍以上。
仅仅适用于聚合类的shuffle操作,适用范围相对较窄。如果是join类的shuffle操作,还得用其他的解决方案。

解决方案五、将reduce join 转成map join
1)适用场景(大表join小表)
在对RDD使用join类操作,或者是在Spark SQL中使用join语句时,而且join操作中的一个RDD或表的数据量比较小,比较适合此方案。
2)实现思路
不使用join算子连接操作,而使用Broadcase变量与map类算子实现join操作,进而完全避免掉shuffle类的操作,

解决方案六:采样倾斜key并拆分join操作
1)适用场景(大表 join 大表)and (少数几个key出现数据倾斜)
如果数据量都比较大,无法采用‘五方案’,那么可以看一下两个RDD/Hive表中的key分布情况。如果出现数据倾斜,其中某一个RDD出现数据倾斜,另一个RDD正常,那么采用这个比较合适的。

解决方案七:使用随机前缀和扩容RDD进行join
1)适用场景(大量的key出现数据倾斜)
如果在进行join操作时,RDD中有大量的key导致数据倾斜,那么进行分拆key也没什么意义,此时就只能使用最后一种方案来解决问题了。
2)实现思路
该方案的实现思路基本和“解决方案六”类似,首先查看RDD/Hive表中的数据分布情况,找到那个造成数据倾斜的RDD/Hive表,比如有多个key都对应了超过1万条数据。
然后将该RDD的每条数据都打上一个n以内的随机前缀。
时对另外一个正常的RDD进行扩容,将每条数据都扩容成n条数据,扩容出来的每条数据都依次打上一个0~n的前缀。
最后将两个处理后的RDD进行join即可。
3)实践经验
曾经开发一个数据需求的时候,发现一个join导致了数据倾斜。优化之前,作业的执行时间大约是60分钟左右;使用该方案优化之后,执行时间缩短到10分钟左右,性能提升了6倍

解决方案八:多种方案组合使用
我们针对出现了多个数据倾斜环节的Spark作业,可以先运用解决方案一和二,预处理一部分数据,并过滤一部分数据来缓解;其次可以对某些shuffle操作提升并行度,优化其性能;最后还可以针对不同的聚合或join操作,选择一种方案来优化其性能。

4.shuffle调优
1.概述:
大多数Spark作业的性能主要就是消耗在了shuffle环节,因为该环节包含了大量的磁盘IO、序列化、网络数据传输等操作。因此,如果要让作业的性能更上一层楼,就有必要对shuffle过程进行调优。

2.shuffle相关参数调优
1)spark.shuffle.file.buffer
默认值 : 32K
参数说明:该参数用于设置shuffle write task的BufferedOutputStream的buffer缓冲大小。将数据写到磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘。
建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如64k)
2)spark.reducer.maxSizeInFlight
默认值: 48m
说明:该参数用于设置shuffle read task的buffer缓冲大小,而这个buffer缓冲决定了每次能够拉取多少数据。
建议:如果作业可用的内存资源较为充足的话,可以适当增加这个参数的大小(比如96m),从而减少拉取数据的次数,
3)spark.shuffle.io.maxRetries
默认值: 3
说明:该参数就代表了可以重试的最大次数。如果在指定次数之内拉取还是没有成功,就可能会导致作业执行失败。
建议:对于那些包含了特别耗时的shuffle操作的作业,建议增加重试最大次数(比如60次)。
4)spark.shuffle.io.retryWait
默认值:5s
参数说明:具体解释同上,该参数代表了每次重试拉取数据的等待间隔,默认是5s。
调优建议:建议加大间隔时长(比如60s),以增加shuffle操作的稳定性。
 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值