Spark性能调优

Spark作业执行流程

一个Spark应用程序由一个driver进程和多个分布在各个节点的executor进程构成。其中,driver负责调度高层次的任务流,产生一系列的job。executor以task的形式执行这些job的task任务,同时存储用户caching的数据。作业的调度通过YARN、Mesos等集群管理应用完成的。

具体而言,当用户提交一个spark作业后,会调用spark内部的action来产生一个spark job,之后spark检查RDD的DAG再计算执行plan,plan由多个stage构成,stage对应一系列的task,task会对不同的数据集进行操作。


关于Spark Shuffle的理解

之前在spark shuffle的博文中谈到过关于spark shuffle的两者方式,即sort-based shuffle和hash-based shuffle。但在所有的spark程序中都会有shuffle的过程吗?当然不是。

具体来说,RDD中包含了固定数目的partition,每个partition包含若干record。例如map,filter,flatmap这一类的操作,返回RDD中一个partition的record只依赖于对父RDD对应partition中的record的计算,这一类的操作一般称之为narrow transformation,因为输入的多个 record 始终来自于有限个partition(一般只为1个)。

相反的,wide transformation的计算所需的多个record来自于父RDD中的多个partiton。例如reduceByKey、groupByKey。相同 key 的元组会被汇聚到同一个 partition 中,被同一个 stage 处理。此时,Spark需要对数据进行 shuffle操作,也就意味着数据需要在集群内传递,最终生成由新的 partition 集合组成的新的 stage。


Spark性能优化

  • 1. 选择开销小的Operator:目标是减少shuffle的次数和shuffle的文件大小
    • 避免使用groupByKey算子,因为该算子需要将所有的数据通过网络传递一遍,网络开销过大,所以能避免则避免;
    • 输入的类型不一致时避免使用reduceByKey,因为产生过多的非必须的对象会严重影响内存的消耗,可使用aggergateByKey进行替代;
    • 利用cogroup代替flatMap-join-groupBy的模式,因为rdd在进行groupBy操作时会有打包解包的开销。
  • 2. 巧妙避免shuffle
    • 当两个数据集需要 join 的时候,利用broadcast variables来避免 shuffle。将数据集在driver中写入到哈希表中,在broadcast到所有的executor中。
  • 3. 适时提高shuffle次数
    • 当输入文件过大,inputFormat产生的partition的数量不够,每个partiiton中又聚集了大量的record,需要使用repartition来增加shuffle的次数,从而提高partition的个数,分担record的负载压力,充分利用集群的CPU能力;
    • 使用treeReduce和treeAggregate 缓解driver merge所有partition输出的压力;
    • 使用aggregation和aggregateByKey对key聚集的数据集进行操作。
  • 4. 使用二次排序
    • 利用 repartitionAndSortWithinPartitions接口把排序推迟到 shuffle 操作中,这使大量的数据有效的输出,排序操作可以和其他操作进行合并。
  • 5. 资源调度分配
    • spark在yarn上运行需要关心CPU和内存资源,做提交spark作业时需设置一系列的参数,参数的设置直接会影响到CPU和内存资源。
      • –executor-cores 指定spark executor拥有核core数目的大小(或在spark-defaults.conf文件中指定,在SparkConf中配置 spark.executor.cores的参数)。core 的数目控制一个 executor 中task的并发数;
      • –executor-memory 指定堆的大小(或在SparkConf中配置 spark.executor.memory的参数)。堆得大小直接影响spark缓存数据的大小,即shuffle的大小;
      • –num-executors 指定需要控制的executor的数目(或在SparkConf中配置spark.executor.instances参数)。可设置 spark.dynamicAllocation.enabled 参数以动态分配 ,使得在有后续积压等待的task时请求 executor,在空闲时释放executor;
    • 对spark on yarn中可调度资源的设置
      • yarn.nodemanager.resource.memory-mb控制每个节点上 container 能够使用的最大内存;
      • yarn.nodemanager.resource.cpu-vcores控制每个节点上 container 能够使用的最大core个数;
      • 在 yarn-client 模式下,应用的 master 运行 driver时默认请求 1024MB的对空间 和 1个core数。在 yarn-cluster 模式中,设置参数 –driver-memory –driver-cores 配置master的资源也同样可以起到优化的作用;
  • 6. 并发调试
    • 在一个spark程序中,task 的个数应该是决定程序性能最重要的参数之一。task 的个数与 stage 中上一个 RDD 的 partition 个数相同,而 RDD 的 partition 个数与被它依赖的 RDD 的 partition 个数相同。由 textFile 和hadoopFile 生成的 RDD 的 partition 个数可通过 parallelize 接口生成的 RDD 的 partition 个数指定。
    • 通过增加partition的数目来增加并发
      • 使用 repartition 选项引发 shuffle;
      • 配置 InputFormat 用户将文件分得更小;
      • 写入 HDFS 文件时使用更小的block;
      • 设置numpartitions的参数 ,不端测试直至性能不再提升为止。每个 task 可用的内存通过:spark.executor.memory * spark.shuffle.memoryFraction * spark.shuffle.safetyFraction)/spark.executor.cores 的公式计算得到。memoryFraction 和 safetyFractio 默认值分别 0.2 和 0.8.
  • 7. 数据结构
    • record记录了数据流的内容,有两种基本的表达方式,即反序列化的 Java 对象和序列化的二进制形式。spark.serializer 控制这两种形式之间的转换的方式。过多的反序列化后的 record 可能会导致数据写入磁盘更加频繁,过多的序列化后的 record 导致更多的 磁盘和网络 IO开销。推荐选择org.apache.spark.serializer.KryoSerializer 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值