本文翻译之cloudera的博客,本系列有两篇,第二篇看心情了

概论

当我们理解了transformation,action和rdd后,我们就可以写一些基础的spark的应用了,但是如果需要对应用进行调优就需要了解spark的底层执行模型,理解job,stage,task等概念。
 
本文你将会了解spark程序是怎么在机器上执行的,同时也学到一些实用的建议关于什么样的执行模型可以提高程序效率

Spark如何执行应用

一个spark程序包括一个driver进程和多个分散在集群节点上的executor进程,driver负责工作流程,executor负责以task方式执行工作同时也会存储一些数据,在程序运行的整个生命周期里driver和executor会一直存活,尽管后期动态资源分配可能会改变这种情况,每个executor都有一些任务槽用于并发运行task,部署这些进程到集群中依赖所选的集群部署方式(standalone,mesos,yarn),但是driver和executor存在每一个spark程序中
当在spark中执行一个action会触发一个spark的job的提交,为了了解该job是什么样的,spark会通过RDD的DAG来确定该action依赖什么,同时生成一个执行计划,该计划从一个最远的RDD开始,通常该RDD不依赖别的RDD或者是一个缓存的数据到一个生成Action结果的最终的RDD
 
执行计划包含了一些stages,stage是由tranformation组装生成的,一个stage对应一组执行相同代码的task,每一个task处理不同的数据,每个stage包含一系列的transformation,这些transformation执行的时候不需要shuffler数据
 
什么决定哪些数据需要进行shuffle, 一个RDD由固定数量的partition组成,每个partition是由许多记录组成的,对于哪些通过窄依赖的transformation(比如map,filter)返回的RDD,它的partition中的一个记录是从
它父RDD中一个parition的一个记录计算得到的,子RDD一个partition只依赖父RDD中的一个partition,像coalesce这种的操作会导致一个task计算父RDD中多个partition,但是仍把他看做窄依赖,因为只依赖一部分的partition.
 
spark还支持像groupbyke和reducebykey这样的宽依赖,这样计算子RDD中一个partition的一条记录依赖父RDD中许多partition中的记录,具有相同key的所有tuple最终会放到在同一个partition中,然后被同一个task处理,为了满足这种操作,spark必须进行shuffle,通过在集群的节点间传输数据最终生成一个新的stage和新的partition集合。(同一个stage中所有的RDD的partition数据应该不一致)
例如
  1. sc.textFile("someFile.txt").
  2. map(mapFunc).
  3. flatMap(flatMapFunc).
  4. filter(filterFunc).
  5. count()
该代码最后执行了一个action的操作,它依赖对数据源是一个文本文件的RDD一系列的转换,该代码会在一个stage中执行,因为他们只窄依赖。
  1. val tokenized = sc.textFile(args(0)).flatMap(_.split(' '))
  2. val wordCounts = tokenized.map((_,1)).reduceByKey(_ + _)
  3. val filtered = wordCounts.filter(_._2 >=1000)
  4. val charCounts = filtered.flatMap(_._1.toCharArray).map((_,1)).
  5. reduceByKey(_ + _)
  6. charCounts.collect()
上面代码将会分成3个stage进行执行,reduceByKey操作会产生一个stage边界,因为计算它的输出需要数据按key进行重新分区。下图是一个RDD的转换图。
 
下面粉色的线划分出需要执行的stage
在每一个stage的边界,父stage中的task会把数据写入到磁盘上,子stage中的task会通过网络去获取数据,因为这样会加重磁盘和网络的负担,stage边界代价太高应该尽可能的避免,父stage中数据的partition可能和子stage中的partition的数据不一致。那么会导致stage边界的转换函数都会接受一个numPartition的参数用于确定子stage中数据的partition个数。
 
对于mapreduce应用来说reducer的个数是一个重要的参数,调整stage边界中partition的个数通常可以提高或降低spark应用的性能,下面讲下如何有效调整该参数。

选择正确的操作

当使用spark编写应用时,开发者可以组装不同的transformation和action生成相同的结果,但是不是所有组装的性能都是一样的,选择正常的组装方式可以大大提供应用的性能,一些规则和观点可以帮助做出正确的选择。
 
ShemaRDD正慢慢稳定下,它将开放Spark的catalyst优化器给使用spark core的开发者使用,它将spark可以做一些高级的选择:哪些操作可以使用,当ShemaRDD变成一个稳定模块后,用户就可以避免做这样的选择了。
 
选择操作的组装方式的目标为了减少shuffle的数量和shuffler数据的数量,这是因为shuffler是一个很昂贵的操作,所有的数据必须落盘然后再通过网络传输,repatition,join,cogroup和别的一些以*key和*bykey结尾的transformation操作都会导致shuffle, 但是性能是不一样的,初学者遇到的最大问题就是不明白他们的操作成本,
  1. 当执行一个associative reductive 操作时不要使用groupbykey,例如。 rdd.groupbykey().mapValues(_.sum)和rdd.reduceBykey(_+_)的结果一样,但是前面的操作会导致所有的数据进行网络传输,后者只会先在本地计算每个patition相同key的和,然后通过shuffler合并所有本地计算的和(都会有shuffle,但是传输的数据减少了很多)
  2. 当输入和输出的类型不一样时不要使用reduceByKey,例如
    当写一个transformation用来找到每一个key对应唯一的一个字符串是,一种方式如下:rdd.map(kv => (kv._1, new Set[String]() + kv._2)).reduceByKey(_ ++ _),该操作会导致大量的不必要的set对象,每个key都会创建一个,这里最好使用aggregateBykey,它会执行map端的聚集更有效
    1. val zero =new collection.mutable.Set[String]()
    2. rdd.aggregateByKey(zero)(
    3. (set, v)=> set += v,
    4. (set1, set2)=> set1 ++= set2)
3.  不要使用flatMap-join-groupBy的模式。当两个数据集已经groupbykey后,如果想join后继续分组,可以使用cogroup,这将建少很多That avoids all the overhead associated with unpacking and repacking the groups.

什么时候shuffle不会发生

需要注意哪些场景下上面的transformation不会有shuffle,spark知道怎么避免shuffle当前面的transformation时数据被同一个patitioner分区过。
  1. rdd1 = someRdd.reduceByKey(...)
  2. rdd2 = someOtherRdd.reduceByKey(...)
  3. rdd3 = rdd1.join(rdd2)
该例子中reduceByKey使用默认的patitioner,所有rdd1和rdd2都是哈希分区的,两个RDD会有两次的shuffler,如果两个RDD的parttion的个数相同,join就不需要额外的shuffle了,因为两个RDD被同一个partitioner分区,相同key的数据都落在两个RDD上的单独的一个parition上。所有rdd3的一个partition只依赖rdd1和rdd2中的单独的partition
注意,这里是任务的执行模型,不是parition的模型,
如果两个RDD的parition使用不同的partitioner,那么join可能需要shuffle
当两个数据集做join时避免shuffle的另一种方式就是使用广播变量,当一个数据集小的足够塞到一个executor内存中,它可以被加载到driver的一个hashtable中然后广播到每一个executor中。一个map的transformation引用该hashtable做join

什么时候需要更多的shuffle

多数情况下shuffle对性能有消耗,但是有时候额外的shuffle可以增大并发而提高性能,例如,数据存储在一个不可切分的文件中(压缩文件),InputFormat进行分区是可能会导致大量的记录在一个partition中,parition的个数少不能有效利用集群计算资源,这种情况下,当数据加载完成后执行repatition操作,它会导致shuffle但是可以利用集群更多的计算资源
 
还一种情况就是使用reduce和aggregate等action在driver上进行数据聚集,当聚集大量数目的partition时,
driver上由于使用一个线程用来合并这些结果将成为性能的瓶颈,为了减轻driver的压力,可以调用reduceBykey和aggregateByKey执行一个分布式聚集操作,把数据切分中更小的parition, 每个partion并行的合并数据,然后把结果返回给driver执行最终的聚集,

二次排序

还一个需要注意的就是repartitionAndSortWithinPartitions这个transformation,该transformation在shuffler的时候就行排序,这样大量数据落盘的时候可以进行排序然后方便后面的操作
 
 
 
 
 
 
 
 
 
 

Spark的job调优(1)的更多相关文章

  1. Spark:性能调优

    来自:http://blog.csdn.net/u012102306/article/details/51637366 资源参数调优 了解完了Spark作业运行的基本原理之后,对资源相关的参数就容易理 ...

  2. Spark的性能调优杂谈

    下面这些关于Spark的性能调优项,有的是来自官方的,有的是来自别的的工程师,有的则是我自己总结的. 基本概念和原则 <1>  每一台host上面可以并行N个worker,每一个worke ...

  3. spark submit参数调优

    在开发完Spark作业之后,就该为作业配置合适的资源了.Spark的资源参数,基本都可以在spark-submit命令中作为参数设置.很多Spark初学者,通常不知道该设置哪些必要的参数,以及如何设置 ...

  4. Spark Streaming性能调优详解

    Spark Streaming性能调优详解 Spark  2015-04-28 7:43:05  7896℃  0评论 分享到微博   下载为PDF 2014 Spark亚太峰会会议资料下载.< ...

  5. spark 资源参数调优

    资源参数调优 了解完了Spark作业运行的基本原理之后,对资源相关的参数就容易理解了.所谓的Spark资源参数调优,其实主要就是对Spark运行过程中各个使用资源的地方,通过调节各种参数,来优化资源使 ...

  6. Spark(九)Spark之Shuffle调优

    一.概述 大多数Spark作业的性能主要就是消耗在了shuffle环节,因为该环节包含了大量的磁盘IO.序列化.网络数据传输等操作.因此,如果要让作业的性能更上一层楼,就有必要对shuffle过程进行 ...

  7. Spark(六)Spark之开发调优以及资源调优

    Spark调优主要分为开发调优.资源调优.数据倾斜调优.shuffle调优几个部分.开发调优和资源调优是所有Spark作业都需要注意和遵循的一些基本原则,是高性能Spark作业的基础:数据倾斜调优,主 ...

  8. Spark的性能调优

    下面这些关于Spark的性能调优项,有的是来自官方的,有的是来自别的的工程师,有的则是我自己总结的. Data Serialization,默认使用的是Java Serialization,这个程序员 ...

  9. Spark Streaming性能调优详解(转)

    原文链接:Spark Streaming性能调优详解 Spark Streaming提供了高效便捷的流式处理模式,但是在有些场景下,使用默认的配置达不到最优,甚至无法实时处理来自外部的数据,这时候我们 ...

  10. Spark Streaming性能调优

    数据接收并行度调优(一) 通过网络接收数据时(比如Kafka.Flume),会将数据反序列化,并存储在Spark的内存中.如果数据接收称为系统的瓶颈,那么可以考虑并行化数据接收.每一个输入DStrea ...

随机推荐

  1. android中MediaPlayer类的用法

    用法直接看sample package com.turtle920.androidaudioprocess; import android.media.MediaPlayer; import andr ...

  2. Java课程设计——坦克大战

    坦克大战——坦克类 一. 团队课程设计博客链接 https://www.cnblogs.com/chenhuilin/p/10275664.html 二.个人负责模块和任务说明 模块:坦克类(玩家坦克 ...

  3. mysql函数之六:mysql插入数据后返回自增ID的方法,last_insert_id(),selectkey

    mysql插入数据后返回自增ID的方法 mysql和oracle插入的时候有一个很大的区别是,oracle支持序列做id,mysql本身有一个列可以做自增长字段,mysql在插入一条数据后,如何能获得 ...

  4. WIN10运行软件,窗口不显示(移动到屏幕外无法复原)的解决办法 Lebal:bug10解决方案

    双显示器切换回单显示器的时候,可能会遇到窗口移动到屏幕外不显示的情况 像这样虽然有缩略图但是点击无反应,并且平铺窗口也不管用,这个时候单击该窗口,Alt+space 执行最小化以及最大化操作即可复原

  5. C#简单操作app.config文件

    即将操作的app.config文件内容如下 <?xml version="1.0" encoding="utf-8"?> <configura ...

  6. 赋予oracle执行存储过程权限和创建表权限

    grant create any table to username; grant create any procedure to username; grant execute any proced ...

  7. ctf中检测和分离隐藏的文件

    使用binwalk检测是否隐藏了文件 root@sch01ar:~# binwalk '/root/桌面/test.jpg' 还藏了一个zip文件,接下来用foremost来分离文件 root@sch ...

  8. Lambda表达式中使用正则表达式

    某语句如果不用正则表达式: string[] names = { "Tom", "Dick", "Harry", "Mary&qu ...

  9. leetcode583

    public class Solution { public int MinDistance(string word1, string word2) { , word2.Length + ]; ; i ...

  10. Java微信公众平台开发(四)--回复消息的分类及实体的创建

    转自:http://www.cuiyongzhi.com/post/42.html 前面有说道对接收到微信服务器消息后对消息的分类,当时主要分为普通消息和事件消息,这里我们要讲述的是我们在给用户回复的 ...