1.为什么要优化
程序运行时间太长,某些task分分钟就跑完了,某些却需要1个甚至几个小时。很显然,这个叫做数据倾斜。
那么我们要做的就是让每一个task都运行差不多的数据,这样速度就快很多了。
2.关于shuffle
shuffle指的是类似reducebykey,groupbykey,或者类似jion这样的操作,指的是将数据从原来的分区转移到另一个分区。
所以呢,shuffle操作是比较花费时间的,因为它涉及到IO,很多人说不建议程序中有shuffle算子。但是我认为,这种只是在数据量不大的情况下,内存计算的时间比IO时间快的多。
那么当数据量巨大时,且RDD分区非常不平衡时,那么想要节约时间,就只能shuffle操作。虽然IO时间长了, 但是总时间大大降低了。
3.命令行优化
首先你得知道集群的一些参数,比如核的总数:64个,内存总量:256G
--driver-memory 10G
--executor-memory 8G
--executor-cores 2
--num-executors 16
--conf spark.default.parallelism=96
--conf spark.storage.memoryFraction=0.5
--conf spark.shuffle.memoryFraction=0.3
driver的内存一般1G就够了,也不怎么用的到,但是当你需要执行take,collect这样的操作,将数据都收集到本地的时候,那么这个就得大些。
executor的内存一般4G-8G,当然你稍微设大一点也行。
num-executors*executor-cores不能超过核总量的一半,1/3最好。
num-executors*executor-memory不能超过内存总量的一半,1/3最好。
当然如果集群中有其他人在跑,那么大家就得商量一下,资源怎么分配。
spark.default.parallelism是设置task的数量的,一般为num-executors*executor-cores的2-3倍,并行化程序决定了你的程序的时间。
spark.storage.memoryFraction是设置持久化内存的占比,默认是0.6,也就是60%,因为我们需要执行shuffle操作,所以这个就稍微小一些,把spark.shuffle.memoryFraction由默认的0.2提到0.3。
4.程序优化
.map(x => {
val random1 = new scala.util.Random().nextInt(960)
(random1, x)
}).repartition(96).map(x => x._2).
将每个元素变成一个map,key是随机数,value是这元素,然后重新分区,这样所有的元素就被打散了,每个分区基本差不多。
.map(x => {
val random1 = new scala.util.Random().nextInt(960)
(random1+ "_" +x._1, x._2)
}).reduceByKey(_ + _).map(x =>( x._1.split("_")(1),x._2))..reduceByKey(_ + _)
先局部,再全局
5.
下面是一个非常全的博客,关于spark优化写的非常好。
http://blog.youkuaiyun.com/u012102306/article/details/51637366