本文翻译之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. uoj #190. 【集训队互测2016】消失的源代码 提交答案题

    Test 1: 发现是一个字母表的映射 把 \('a' \to 'z'\) 打进去找出映射就好了QAQ . Test 2: 求助 \(dalao\) 得知的点.. 答案是 : \(2016x^2 + ...

  2. python操作rabbitmq操作数据(不错)

    ##一.RabbitMQ 消息队列介绍 RabbitMQ也是消息队列,那RabbitMQ和之前python的Queue有什么区别么? py 消息队列: 线程 queue(同一进程下线程之间进行交互) ...

  3. fn project hot functions 说明

    1. 简单介绍 所谓 hot  functions 实际上就是长时间运行的functions ,简单理解类似后台任务 2. fnproject  处理的方式 fnproject 使用 类似 http的 ...

  4. C++输入流和输出流、缓冲区

    一.C++输入流和输出流 输入和输出的概念是相对程序而言的. 键盘输入数据到程序叫标准输入,程序数据输出到显示器叫标准输出,标准输入和标准输出统称为标准I/O,文件的输入和输出叫文件I/O. cout ...

  5. junit基础学习

    学习地址一:http://blog.csdn.net/andycpp/article/details/1327147/ 学习地址二:http://blog.csdn.net/zen99t/articl ...

  6. zabbix监控的配置

    ZABBIX监控的操作步骤有两个! 首先登录到zabbix 的主界面在configuration---host---create host在如上的host 创建界面中 主要是输入被监测的server的 ...

  7. contOS 下安装mysql

    一.mysql简介 说到数据库,我们大多想到的是关系型数据库,比如mysql.oracle.sqlserver等等,这些数据库软件在windows上安装都非常的方便,在Linux上如果要安装数据库,咱 ...

  8. GOF23设计模式之建造者模式(builder)

    一.建造者模式概述 建造者模式的本质: 1.分离了对象子组件的单独构造(由Builder负责)和装配(由Director负责).从而可以构造出复杂的对象.这个模式适用于:某个对象的过程复杂的情况下使用 ...

  9. Java-Maven-Runoob:Maven Eclipse

    ylbtech-Java-Maven-Runoob:Maven Eclipse 1.返回顶部 1. Maven Eclipse Eclipse 提供了一个很好的插件 m2eclipse ,该插件能将 ...

  10. Linux学习笔记 - Shell 运算符篇

    Shell 运算符分类 Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符 关系运算符 布尔运算符 字符串运算符 文件测试运算符 算数运算符 首先,使用 shell 算数运算符是,需要 ...