Spark优化总结

Spark优化

主要分为两个方面的优化,一是代码逻辑的优化,二是资源配置的优化

1.代码逻辑

1.1.RDD优化

RDD优化主要也有两个方面的考虑,一是RDD的复用,二是RDD的持久化。那么主要针对RDD的持久化进行说明。在Spark中多次对同一个RDD执行算子时,每次都会对这个RDD的父RDD重新计算一次,所以要避免这种重复计算的资源浪费,那么就需要对RDD进行持久化。

Memory_Only 内存
Memory_Only_2 内存+数据备份
Memory_Only_Ser

内存+序列化

Memory_And_Disk 内存+磁盘
Memory_And_Disk_2 内存+磁盘+备份
Memory_And_Disk_Ser 内存+磁盘+序列化
Disk_Only 磁盘

1.2.并行度优化

基本概念:

  • CPU核数

一台服务器有两颗CPU,每颗CPU是16核,一颗CPU的超线程数是2,所以:

假设我们的集群是100台服务器那么:

总核数 = 100 * 2 * 16 = 3200

总的逻辑CPU数 = 100 * 2 * 16 * 2 = 6400

  • RDD分区

默认参数:spark.files.maxPartitionBytes = 128 M,就是说当一个文件大小超过128M才会拆成两个分区,而当SparkContext创建后会生成两个参数

sc.defaultParallelism = spark.default.parallelism

sc.defaultMinPartitions = min(spark.default.parallelism,2)

也就是说,我们可以根据这两个参数来推算RDD的分区数量。当读取本地文件时:默认RDD分区公式: max(本地file的分片数, sc.defaultMinPartitions)。当读取HSFS文件时:默认RDD分区公式:max(hdfs文件的block数目, sc.defaultMinPartitions)

 

Spark并行度:

Spark并行度就是每个Spark作业中各个Stage中所有task的数量,也就表示了Spark作业在各个阶段的并行度,需要知道的是DAGScheduler会根据Spark作业中是否存在Shuffle Dependency来划分Stage,然后为每个Stage中的Task组成TaskSet交给TaskScheduler,那么TaskScheduler会将TaskSet发送到Executor进行处理,当Executor接受到TaskSet之后进行反序列化之后把这些Task封装到TaskRunner的线程中执行。Spark并行度是由参数spark.default.parallelism所控制的,而默认的并行度分为这么几种:

  • 本地模式

在local[n]模式下:spark.default.parallelism = n

  • 集群

在Sandalone/Yarn模式下:spark.default.parallelism = 所有Executor数乘以每个Executor的core数,也就是所有Executor总的core数

 

总结:

比如:集群规模是100台,每台服务器两颗CPU,每颗CPU16核,那么总的CPUcores就是6400个,如果在计算的任何stage中使用的并行task的数量没有足够多,那么集群资源是无法被充分利用。所以为了充分利用系统资源尽可能提高。

  • 参数一:num-executors

该参数用于设置Spark作业总共需要用多少个Executor来执行

  • 参数二:executor-cores

该参数用于设置每个Executor进程的CPU core数量

  • 参数三:spark.default.parallelism

该参数用于设置每个Stage默认的Task数量

 

补充:

每个几点可以启动一个或多个Executor,每个Executor由一个或多个core组成,每个core只能执行一个task,每个task执行的结果产生一个目标partition


1.3.广播变量

广播变量其实就是将ReduceJoin转化为MapJoin,这样做的好处就是将Reduce端每个Task获取一份副本,转化为一个Executor获取一份副本,直接减少了内存开销,同时也避免了数据倾斜

def broadcastVal(sc: SparkContext): Unit = {
    val kv = Map(("a",1),("b",2))
    val bc = sc.broadcast(kv)
    val rdd = sc.textFile("D:/io/input/txt/rdd.txt").flatMap(_.split("\\s+"))
    rdd.foreach{
        e=>{
            if (bc.value.contains(e)) {
                println(e)
            }
        }
    }
}

通常来说只会将数据进行广播,RDD并不能广播,如果想将RDD广播出去的话,可以将RDD拉回到Driver然后将RDD的数据在广播出去

1.4.Kryo序列化

从Spark 2.0.0版本开始,简单类型、简单类型数组、字符串类型的Shuffling RDDs 已经默认使用Kryo序列化方式了。或者手动指定conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer"); 

1.5.本地化等待时长

1.6.算子优化

1.6.1.mapPartitions
1.6.2.foreachPartition
1.6.3.filter&coalesce
1.6.4.repartition
1.6.5.reduceByKey

2.资源配置

2.1.最优资源配置

--num-executors executor个数
--driver-memory Driver内存
--executor-cores ExecutorCPU数量
--executor-memory Executor内存

2.2.shuffle优化

2.2.1.Map缓冲区

默认32KB,手动指定:conf.set("spark.shuffle.file.buffer", "64")

2.2.2.Reduce缓冲区

默认48M,手动指定:conf.set("spark.reducer.maxSizeInFlight", "96")

2.2.3.Reduce重试次数

默认3次,手动指定:conf.set("spark.shuffle.io.maxRetries", "6")

2.2.4.Reduce等待间隔

默认5s,手动指定:conf.set("spark.shuffle.io.retryWait", "60s")

2.2.5.SortShuffle

默认200,手动指定:conf.set("spark.shuffle.sort.bypassMergeThreshold", "400")

2.3.JVM优化

对于JVM调优,首先应该明确,major gc/minor gc,都会导致JVM的工作线程停止工作,即stop the world

Spark 1.6 之后引入的统一内存管理机制,与静态内存管理的区别在于存储内存和执行内存共享同一块空间,可以动态占用对方的空闲区域,统一内存管理的堆内内存结构

2.3.1.Cache内存占比

静态内存管理机制:默认0.6,手动指定:conf.set("spark.storage.memoryFraction", "0.4")

统一内存管理机制:无需手动调节(动态占用机制)

2.3.2.Executor对外内存

默认300M,手动指定:--conf spark.yarn.executor.memoryOverhead=2048

2.3.3.连接时长

默认60s,手动指定:--conf spark.core.connection.ack.wait.timeout=300

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章