Spark优化

我的梦境 提交于 2020-02-06 10:30:45

Spark优化总结

1.资源调优

  • 在部署spark集群时指定资源分配的默认参数(配置文件)
    • spark安装包的conf下spark-env.sh
    • SPARK_WORKER_CORES
    • SPARK_WORKER_MEMORY
    • SPARK_WORKER_INSTANCES 每台机器启动的worker数
  • 在提交Application的时候给当前的appliation分配更多的资源(liunx提交命令)
    • 提交命令选项
    • –executor -cores (不设置,默认每一个worker为当前application开启一个executor,这个executor会使用这个Worker的所有cores和1G内存)
    • –executor-memory
    • –total-exexutor-cors (不设置,默认将集群剩下的所有的核数分配给当前application)
  • Application的代码中设置或在Spark-default.conf中设置(代码中设置)
    • spark.executor.cores
    • spark.executor.memory
    • spark.max.cores
    • 动态分配资源
      • spark.shuffle.service.enableed true //启动external shuffle Service服务
      • spark.shuffle.service.port 7377 //shuffle的服务端口为7377(端口要与yarn-site中一致)
      • spark.dynamicAllocation.enable true //开启动态资源分配
      • spark.dynamicAllocation.minExecutor 1 //每个application最小分配的executor数
      • spark.dynamicAllocation.maxExecutor 30 //每个application最大并发分配的executor数
      • spark.dynamicAllocation.schedulerBacklogTimeout 1s //如果有新任务处于等待状态,并且等待超过默认时间(默认1s),则会依次启动executor,每次启动1,2,4,8…个executor(如果有的话)。
      • spark.dynamicAllocation.sustainedSchedulerBacklogTimeout 5s //启动的间隔由控制(默认与schedulerBacklogTimeout相同)

2.并行度调优

  • 读取hdfs数据时,降低block大小,相当于提高了RDD中partition的个数 sc.textFile(xx,numPartitions)
  • sc.parallelize(xxx.numPartitions)
  • sc.makeRDD(xxx,numpartitions)
  • sc.parallelizePairs(xxx,numpartitions)
  • repartions/coalesce //增加或减少partition会产生shuffle,coalesce减少分区可以不产生shuffle
  • reducebykey/groupbykey/join —(xxx,numpartitions)
  • spark.default.parallelism net set
  • spark.sql.shuffle.partitions–200
  • 自定义分区数
  • 如果数据SparkStreaming中
    • Receiver: spark.streaming.blockInterval—200ms
    • Direct:读取的topic数

3.代码调优

  • 避免重复的RDD,(读取的是同一份文件,却创建了两个RDD)

    • val rdd1 = sc.textFile(path1)
    • val rdd2 = sc.textFile(path1)
  • 对需要多次使用的RDD进行持久化

    • MEMORY_ONLY
      • 内存足够大时使用(仅持久化到内存),
    • MEMORY_ONLY_SER
      • 该级别会将RDD数据序列化后存到内存中,此时每个partition仅仅是一个字节数组,这种级别会多出来序列化与反序列化的资源开销(主要使用计算机的cpu资源).拿cpu换取内存
    • MEMORY_AND_DISK_SER
      • 优先尽量尝试将数据缓存在内存中,内存缓存不下才会写入磁盘
  • 持久化算子

    • cache
      • MEMORY_ONLY
    • persist
      • MEMORY_ONLY
      • MEMORY_ONLY_SER
      • MEMORY_AND_DISK_SER
    • checkpoint
      • 如果一个RDD的计算时间比较长或者计算起来比较复杂,一般将这个RDD的计算结果保存到HDFS上,这样数据会更加安全
      • 如果一个RDD的依赖关系非常长,也会使用checkpoint,会切断依赖关系,提高容错的效率
  • 尽量避免使用shuffle类的算子

    • 可以使用广播变量来模拟join,使用场景:一个大RDD,一个小RDD
    • join算子=广播变量+filter.广播变量+map ,广播变量+flatmap
  • 使用map-side于预聚合的shuffle操作

    • 尽量使用combiner的shuffle类算子

      • combiner

        • 在map端,每一个map task计算完毕后进行局部聚合.
      • 优点

        • 降低shuffle write写磁盘的数量
        • 降低shuffle read拉取数据的大小
        • 降低reduce端聚合的次数
      • combiner算子

        • reduceByKey 在一定的场景下可以代替groupbyKey
        • aggregateByKey (默认两个方法,有一个默认值)map端可reduce端可以设置计算逻辑,但是它是有计算默认值的
        • cominerByKey (默认三个方法,第一个是基础参数)map端可reduce端可以设置计算逻辑,但是它是有计算默认值的
  • 使用高效能算子

    • reducebykey代替groupbykey
    • mappartition代替map
    • foreachpartition代替foreach
    • filter后使用coalesce减少分区数
    • repartitionAndSortWithinPartitions代替repartition与sort
    • repartition和coalesce算子操作分区
  • 使用广播变量

    • 需要在算子函数中使用 外部变量时可以使用广播变量,不使用广播变量时,一个Executor中有多个task任务,这些task需要使用外部变量时,这时driver将变量发送到每个task中,这样导致每个task都有这个变量,造成性能浪费.
    • 使用广播变量,变量的副本数与executor个数相同,不使用变量的副本数与task相同
  • 使用kryo优化序列化性能

    • spark中使用到序列化
      • 在算子函数中使用到外部变量时,该变量会被序列化后进行网络传输
      • 将自定义的泛型作为RDD的泛型类型时(javaRDD),所有的自定义类型对象,都会进行序列化,也要要求自定义的类必须实现Serialiable接口
      • 持久化策略使用MEMORY_ONLY_SER时,Spark会将RDD中的每个partition都序列化成一个大的字节数组
    • kryo序列化优化以后,可以让网络传输的数据减少,在集群中耗费的内存减少
    • kryo序列化使用的前提是,所有的需要进行序列化的自定义类型需要注册
    • spark中使用kryo
Sparkconf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
.registerKryoClasses(new Class[]{SpeedSortKey.class})
  • 优化数据结构
    • 消耗内存的3中现象
      • 对象,(有对象头、引用等额外的信息)
      • 字符串(有一个字符数组以及长度等额外信息)
      • 集合类型(内部通常会使用一些内部类来封装集合元素)
    • 尽量使用字符串替代对象,使用原始类型(比如Int、Long)替代字符串,使用数组替代集合类型
  • 使用fasteutil
    • fastutil的每一种集合类型,都实现了对应的Java中的标准接口(比如fastutil的map,实现了Java的Map接口),因此可以直接放入已有系统的任何代码中

4.数据本地化

  • PROCESS_LOCAL

    • task要计算的数据在本进程的内存中

在这里插入图片描述

  • NONE_LOCAL

    • task计算的数据在本节点的磁盘上
    • task计算的数据在本节点的其他executor进程的内存中

在这里插入图片描述

  • NO_PREF

    • task要计算的数据在关系型数据库中

在这里插入图片描述

  • RACK_LOCAL

    • task要计算的数据在同机架的不同节点的磁盘后executor进程的内存中

在这里插入图片描述

  • ANY

    • 不同机架
  • 本地化调优

    • taskscheduler发送task任务时,需要依据数据的位置来分发,如果TaskScheduler分发的task在默认3·5次,如果依然无法执行,那么TaskScheduler会降低一级数据本地化的级别再次发送task
    • 提高数据本地化级别
      • 可以增加每次发送task的等待时间(默认都是3s),将3s倍数调大

5.内存调优

  • JVM堆内存中有两大部分,年轻代,老年代
  • 年轻代中有3部分组成,Eden,两个Survivor,每次只使用Eden和一个Survivor
  • 年轻代中会进行小GC,将没有引用的对象清理掉,同时将活下来的对象一次性复制到另一个Survivor.
  • 如果存活下来的对象大小大于Survivor2的大小,那么JVM会将多余的对象放入老年代中,默认15次小GC孩存在的对象也会存放在老年代中
  • 老年代中应该存放的是数量比较少但会长期使用的对象(数据库连接池).但是以上会导致老年代中对象过多,这样会导致full GC.
  • 不管是小GC,还是full gc都会导致JVM的工作线程停止

在这里插入图片描述

  • 总结(内存不足)
    • 频繁的minor gc
    • 老年代中存在大量的短生命周期的对象,导致full gc
    • gc 过多会导致spark的性能和运行速度降低
  • 调优
    • 以静态内存管理为例,(RDD的缓存数据和广播变量,shuffle的聚合内存,task运行内存)
    • 提高Executor总体内存的大小
    • 降低存储内存比例或shuffle聚合内存比例

6.shuffle 调优

  • buffer大小——32KB

  • shuffle read拉取数据量的大小——48M

  • shuffle聚合内存的比例——20%

  • 拉取数据重试次数——5次

  • 重试间隔时间60s

  • Spark Shuffle的种类

  • SortShuffle bypass机制 200次

7.调节Executor的堆外内存

  • spark底层shuffle的传输方式是使用netty(零拷贝)传输,netty在进行网络传输的过程中,会申请堆外内存.

  • 堆外内存默认大小为每一个executor的内存10%大小,

  • 堆外内存过小时,会导致spark作业反复崩溃,无法运行

  • 所以要将堆外内存调大

    • 在./spark-submit提交任务的脚本里面添加

      yarn下:

      ​ --conf spark.yarn.executor.memoryOverhead=2048 单位M

      standalone下:

      ​ --conf spark.executor.memoryOverhead=2048单位M

  • executor在进行shuffle write,优先从自己的本地mapoutputworker中获取,没有就好通过transferService,去远程连接其他节点上executor的block manager去获取,尝试建立远程连接.但是如果那个executor因为对象太多而导致正在gc,所有的工作进程全部停止,无法提供响应.

  • spark默认的网络连接的超时时长是120s;如果卡住120s都无法建立连接的话,那么这个task就失败了

  • 所以调节等待时长

    • 在./spark-submit提交任务的脚本里面添加:

      –conf spark.core.connection.ack.wait.timeout=300

8.解决数据倾斜

  • 使用Hive ETL预处理数据
  • 过滤少数导致倾斜的key
  • 提高shuffle操作的并行度
  • 双重聚合

在这里插入图片描述

  • 使用广播变量代替join
  • 使用倾斜key并分析join操作

在这里插入图片描述

  • 使用随机前缀和扩容RDD进行join

在这里插入图片描述

9.Spark故障解决

  • shuffle file cannot find:磁盘小文件找不到。

    • connection timeout ----shuffle file cannot find
    • 提高建立连接的超时时间,或者降低gc,降低gc了那么spark不能堆外提供服务的时间就少了,那么超时的可能就会降低。
    • fetch data fail ---- shuffle file cannot find
    • 提高拉取数据的重试次数以及间隔时间。
    • OOM/executor lost ---- shuffle file cannot find
    • 提高堆外内存大小,提高堆外内存大小。
  • reduce OOM

    • BlockManager拉取的数据量大,reduce task处理的数据量小

      解决方法:

    • 降低每次拉取的数据量

    • 提高shuffle聚合的内存比例

data fail ---- shuffle file cannot find

  • 提高拉取数据的重试次数以及间隔时间。

  • OOM/executor lost ---- shuffle file cannot find

  • 提高堆外内存大小,提高堆外内存大小。

  • reduce OOM

    • BlockManager拉取的数据量大,reduce task处理的数据量小

      解决方法:

    • 降低每次拉取的数据量

    • 提高shuffle聚合的内存比例

    • 提高Executor的内存大小

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!