集群环境
集群环境可以看到集群一个有280G内存,120核数,5个节点
spark-submit --
启动命令含义:
spark-submit class cn.dmp.tools.Bzip2Parquet \类的main方法所在类
--master yarn
--deploy-mode cluster \ 运行模式
--driver-memory 4g \ 共280g
--executor-memory 8g \ 每个executor使用的内存大小,一台机器可以有多个executor,
--executor-cores 4 \ 共120核数
/root/dmp.jar \jar 包
/adloga/biz2/* \输入路径
/parquet 20 1 \输出路径,20:执行20次
& 后台启动命令:
第一次测试:executor-cores 4:并行
nohup spark-submit class cn.dmp.tools.Bzip2Parquet --master yarn --deploy-mode cluster --driver-memory 4g --executor-memory 8g --executor-cores 4 /root/dmp.jar /adloga/biz2/* /parquet &
观察sparkUI
1. 发现gc时间不长
2. 一片绿色效果好,红色序列化时间,没有shuffle时间,四个程序一起并行(因为有四个核数)
3. 总共运行时间
第二次测试:executor-cores 1,串行
nohup spark-submit class cn.dmp.tools.Bzip2Parquet --master yarn --deploy-mode cluster --driver-memory 4g --executor-memory 8g --executor-cores 1 /root/dmp.jar /adloga/biz2/* /parquet &
发现是单线程跑
运行时间
第三次测试:num-executors :executors的数量,8个block,20个executors
nohup spark-submit class cn.dmp.tools.Bzip2Parquet --master yarn --deploy-mode cluster --driver-memory 4g --executor-memory 8g --executor-cores 1 --num-executors 20 /root/dmp.jar /adloga/biz2/* /parquet &
结果:有的executors 没有任务,一个block对应一个task,所以集群中还是只有8个executors 在工作
第四次:重新分区
nohup spark-submit class cn.dmp.tools.Bzip2Parquet --master yarn --deploy-mode cluster --driver-memory 4g --executor-memory 8g --executor-cores 1 --num-executors 20 /root/dmp.jar /adloga/biz2/* /parquet 20 5 &
上面20:指分区个数,20个分区一个分区对应一个executors
我们现在5个节点,有20个task,说明一个节点上面有多个executors ,而每个executors 上面会有多个task
重新分区会有shuffle,
结果:有的executors会有多个task,
时间:
job有shuffle就一定慢么? 不一定,因为并行度也会增加
分区数=核数的2倍到3倍
第四次:调整任务数,核数,达到最优
1.
nohup spark-submit class cn.dmp.tools.Bzip2Parquet --master yarn --deploy-mode cluster --driver-memory 4g --executor-memory 4g --executor-cores 2 --num-executors 50 /root/dmp.jar /adloga/biz2/* /parquet 300 &
共120个核数,操作系统 = 4个核数(4*5=20) 所以任务数是300 (2到3倍)
300个patation,每个分区一个核数
num-executors:共280个g,1个executor-memory= 4g(yarn在调度的时候1g),driver = 4g, 280-4=276, 276/5=55.2 所以50个
结果:只申请了45个
时间:
并行度:2
nohup spark-submit class cn.dmp.tools.Bzip2Parquet --master yarn --deploy-mode cluster --driver-memory 4g --executor-memory 8g --executor-cores 5 --num-executors 20 /root/dmp.jar /adloga/biz2/* /parquet 300 &
分区数时间有限
时间:差别不是很明显
总结:
增加任务的并行度
executor-memory 8g :
(executor-memory +1) * num-executors <=集群总的内存大小
总乘积的时候,executor-memory要多加一个g
executor-cores 5:
executor-cores * num-executors <=集群总的核数
一个executor如果只分配了一个核数,并行度就是1,如果分配了n个核数,那么并行的最大数量就是n,
num-executors 20 :申请的总的executor数量,executor数量和分区数量成倍数。
partitionNumber:最好是集群核数的2到3倍
相关参数
进入spark安装目录的conf目录
cp spark-defaults.conf.template spark-defaults.conf
1.spark.shuffle.manager=hash #Spark 1.2的默认Shuffle机制从Hash变成了Sort
应用场景:当不需要进行排序且产生的临时文件不是很多时,性能可能会比sort shuffle要好。
2.spark.shuffle.spill=false #全程在内存跑,只有在确定内存足够使用时,才可以将这个选项设置为false。
这个参数的默认值是true,用于指定Shuffle过程中如果内存中的数据超过阈值时是否需要将部分数据临时写入外部存储。
3.spark.shuffle.memoryFraction=0.5 #决定了当Shuffle过程中使用的内存达到总内存多少比例的时候开始spill
#这个值默认是0.2。此参数可以适当调大,可以控制在0.4~0.6。可以适当调大此值,可以减少磁盘I/O次数
4.spark.shuffle.blockTransferService=netty #用于在各个Executor之间传输Shuffle数据。
5.spark.shuffle.consolidateFiles = true #为了解决在Hash Based Shuffle过程中产生过多文件的问题,配置为true,对于同一个Core上运行的Shuffle Map Task不会产生一个新的Shuffle文件而是重用原来的。
6.spark.shuffle.compress=true #对最终写入本地文件系统的输出文件进行压缩
spark.shuffle.spill.compress = true #最终的shuffle输出文件进行压缩
#处理的中间结果在spill到本地硬盘时都会进行压缩,在将中间结果取回进行merge的时候,要进行解压
#压缩,省带宽,但是消耗cpu,两者需要衡量。(比如集群普遍使用的是千兆网络,那么将这个选项设置为true可能更合理;如果计算是CPU密集型的,那么将这个选项设置为false可能更好。)
优化
1.更好的序列化实现:更换默认的序列化机制,由原生的java序列化变为kryo,
原因
- java的序列化有两个问题,一个是性能相对比较低,另外它序列化完二进制的内容长度也比较大,造成网络传输时间拉长
- kryo,比java的序列化快10倍以上。而且生成内容长度也短
实现方式
方法一:修改spark-defaults.conf配置文件
设置: spark.serializer org.apache.spark.serializer.KryoSerializer #注意:用空格隔开
方法二:启动spark-shell或者spark-submit时配置
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer
方法三:在代码中
val conf = new SparkConf()
conf.set(“spark.serializer”,“org.apache.spark.serializer.KryoSerializer”)
三种方式实现效果相同,优先级越来越高。
2.配置spark.local.dir=/home/tmp,/home/tmp2;可以创建多个文件夹,每个文件夹都对应一个真实的硬盘。当产生临时文件发送磁盘I/O时,可以提高性能。
3.慎用collect:1.速度慢,2.内存溢出,会将分散到不同服务器的数据汇聚到一台服务器,一般可以将结果存储在HDFS上
4.使用MapPartitions替代map
5.启用推测执行机制。可以设置spark.speculation true
开启后,spark会检测执行较慢的Task,并复制这个Task在其他节点运行,最后哪个节点先运行完,就用其结果,然后将慢Task 杀死
6.GC调优,spark是一种高度依赖内存的框架,针对GC调优也很重要,可以看一下这篇文章:垃圾(GC)回收机制及算法以及配置调优
总结
对于大量依赖于内存计算的Spark应用,GC调优显得尤为重要。在发现GC问题的时候,不要着急调试GC。而是先考虑是否存在Spark进程内存管理的效率问题,例如RDD缓存的持久化和释放。至于GC参数的调试,首先我们比较推荐使用G1 GC来运行Spark应用。相较于传统的垃圾收集器,随着G1的不断成熟,需要配置的选项会更少,能同时满足高吞吐量和低延迟的寻求。当然,GC的调优不是绝对的,不同的应用会有不同应用的特性,掌握根据GC日志进行调优的方法,才能以不变应万变。最后,也不能忘了先对程序本身的逻辑和代码编写进行考量,例如减少中间变量的创建或者复制,控制大对象的创建,将长期存活对象放在Off-heap中等等。