Spark

spark的相关问题:

一、 spark中的RDD是什么, 有哪些特性

  RDD(Resilient  Distributed  Dataset)叫做分布式数据集, 是Spark中最基本的数据抽象, 它代表一个不可变,可分区,里面元素可并行计算的集合。

    Dataset: 就是一个集合, 用于存放数据的。

    Distributed:  分布式, 可以并行在集群中计算

    Resilient:  表示弹性的

      弹性表示

        1. RDD 中的数据可以储存在内存或者是磁盘中

        2. RDD中的分区是可以改变的

  五大特性:

  1、 A list of  partitions

    一个分区列表, RDD中的数据都存放在一个分区列表里面

  2、 A function for computing each split

    作用在每一个分区中的函数

  3、 A list of dependencies on other RDDs

    一个RDD依赖于多个RDD, 这个点很重要, RDD的容错机制就是依据这个特性而来的

  4、Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)

    可选的, 针对于kv类型的RDD才具有这个特性, 作用是决定了数据的来源以及数据处理后的去向。

  5、 Optionally, a list of preferred localtions to compute each split on (e.g. block locations for an HDFS file)

    可选项, 数据本地性, 数据位置最优

 

二、概述一下spark中的常用算子区别(map、mapPartitions、foreach、foreachPartition)

    map: 用于遍历RDD, 将函数f应用于每一个元素, 返回新的RDD(transformation算子)

    foreach:  用于遍历RDD, 将函数f 应用于每一个元素, 无返回值 (action算子)。

    mapPartitions:  用于遍历操作RDD中的每一个分区, 返回生成一个新的RDD(transformation算子)

    foreachPartition;  用于遍历操作RDD中的每一个分区, 无返回值, (action 算子)

  总结:

    一般使用mapPartitions或者foreachPartition 算子 比 map 和foreach 更加有效, 推荐使用。

 

三、谈谈spark中的宽窄依赖

  RDD和它依赖的父RDD(s)的关系有两种不同的类型, 即窄依赖(narrow dependency)和宽依赖(wide  dependency)。

  宽依赖:  指的是多个子RDD的Partition会依赖同一个父RDD的Partition。

  窄依赖:  值得是每一个父RDD的Partition最多被子RDD的一个Partition使用。

 

四、spark中如何划分stage

  1、 Spark Application 中可以因为不同的Action 触发众多的job, 一个Application 中可以有多个job, 每一个job 是由一个或者多个Stage构成的, 后面的Stage 依赖于前面的Stage, 也就是说只有前面依赖的Stage计算完毕后,  后面的Stage才会运行。

  2、Stage 划分的依据就是宽依赖, 何时产生宽依赖, 例如, reduceByKey, groupByKey的算子, 会导致宽依赖的产生。

  3、 由Action (例如collect)导致了SparkContext.runJob的执行, 最终导致了DAGScheduler中的submitJob的执行, 其核心是通过发送一个case class JobSubmitted对象给eventProcessLoop。   eventProcessLoop是DAGSchedulerEventProcessLoop的具体实例, 而DAGSchedulerEventProcessLoop是eventLoop的子类, 具体实现EventLoop的onReceive方法, onReceive方法转过来回调doOnReceive

  4、在doOnReceive中通过模式匹配的方法吧执行路由到

  5、在handleJobSubmitted中首先创建finalStage, 创建finalStage时候会建立父Stage的依赖链条。

 

  总结:

   一直以来是从代码的逻辑层面上来展开说的, 可以简单点说 : 写介绍什么是RDD中的宽窄依赖, 然后在根据DAG有向无环图进行划分, 从当前job 的最后一个算子往前推, 遇到宽依赖, 那么当前在这个批次中的所有的算子操作都划分成一个stage,然后继续按照这种方式再继续往前推, 如再遇到宽依赖, 又划分一个stage, 一直到最前面的一个算子, 最后整个job会被划分成多个stage, 而stage之间又存在依赖关系, 后面的stage依赖前面的stage。

 

五、spark-submit的时候如何引入外部jar包

  在通过spark-submit 提交任务时, 可以通过添加配置参数来指定

  -driver-class-path 外部jar包

  -jars 外部jar包

 

六、 spark如何防止内存溢出

  1、 driver端内存溢出:

    •  可以增大driver 的内存参数: spark.driver.memory(defaulelg)

    •  这个参数可以用来设置Driver 的内存, 在spark程序中, SparkContext, DAGScheduler都是运行在Driver端的, 对应rdd的Stage切分也是在Driver端运行, 如果用户自己写的程序有过多的步骤, 切分出过多的Stage, 这部分信息消耗的是Driver的内存, 这个时候就需要调大 Driver 的内存。

  2、map过程产生大量对象导致内存溢出

    • 这种溢出的原因是在单个map中产生了大量的对象导致的, 例如: rdd.map(x=>for(i <- 1 to 10000) yield   i.toString), 这个操作在RDD中, 每个对象都产生了10000个对象, 这肯定很容易产生内存溢出的问题。  针对这种问题, 在不增加内存的情况下, 可以通过减少每个Task的大小, 以便达到每个Task 即使产生大量对象的Executor 的内存也能够装的下,  具体做法可以在会产生大量对象的map操作之前调用repartition方法, 分区成更小的块传入map。例如:

  rdd.repartition(10000).map(x=>for(i <- 1 to 10000) yield i.toString).

 面对这种问题注意: 不能使用rdd.coalesce方法, 这个方法只能减少分区, 不能增加分区, 不会有shuffle的过程。

  

  3、数据不平衡导致内存溢出

    • 数据不平衡除了有可能导致内存溢出外, 也有可能导致性能的问题, 结局方法和上面说的方法类似, 技术调用repartition重新分区, 这里就不在累赘了。

 

  4、shuffle后内存溢出

    •  shuffle 内存溢出的情况可以说都是shuffle后, 单个文件过大导致的, 在Spark中, join, reduceByKey这一类型的过程, 都会有shuffle的过程, 在shuffle的使用, 需要传入一个partitioner, 大部分Spark中的shuffle 操作, 默认的partitioner都是HashPatitioner, 大部分Spark中的shuffle操作, 默认的partitioner都是HashPatitioner, 默认值是父RDD中最大的分区数, 这个参数通过spark.default.parallelism控制(在spark-sql中用spark.sql.shuffle.partitions),spark.default.parallelism或者自己实现的Partitioner就不能使用spark.default.parallelism这个参数来控制shuffle的并发量。 如果是别的partitioner 导致的 shuffle内存溢出, 就需要从 partitioner的代码增加partitions的数量。

  5、standalone的模式下资源分配不均匀导致内存溢出

    •   在standalone的模式下, 如果配置了 -total-executor-cores 和 -executor-memory 这两个参数, 但是没有配置 -executor-cores这个参数的化, 就有可能导致, 每个Executor的memory 是一样的, 但是 cores 的数量不通过,那么在 cores 数量多的 Excetor 中, 由于能够同时执行多个Task, 就很容易导致内存溢出的情况。  这种情况的解决方法就是同时配置 -executor-cores 或者 spark.executor.cores 参数, 确保Executor资源分配均匀。

    •  使用rdd.persist(StorageLevel.MEMORY.AND.DISK.SER) 代替rdd.cache().

    •  rdd.cache() 和 rdd.persist(Storage.MEMORY.ONLY) 是等价的, 在内存不足的时候,rdd.cache() 的数据会丢失, 再次使用的时候会重算, 而rdd.ppersisit(StorageLevel.MEMORY.AND.DISK.SER) 在内存不足的时候会存储在磁盘, 避免重算, 知识消耗点IO时间。

 

七、spark中cache和persist的区别

  1、cache: 缓存数据, 默认是缓存在内存中, 其本质还是调用persist

  2、persist: 缓存数据, 有丰富的数据缓存策略。  数据可以保存在内存, 也可以保存在磁盘中 , 使用的时候指定对应的缓存级别就可以了。

八、spark中的数据倾斜的现象、原因、后果

  1、数据倾斜的现象

    多数task执行速度较快, 少数task执行时间非常长, 或者等待很长时间后提示你内存不足, 执行失败。

  2、数据倾斜的原因

    • 数据问题

      1. key本身分布不均衡(包括大量的key为空)

         2. key的设置不合理

    • spark使用问题

       1. shuffle 时的并发度不够哦

          2. 计算方式有误

  3、数据倾斜的后果

    1. spark中的 stage 的执行时间受限于最后那个执行完成的 task ,因此运行缓慢的任务会拖垮整个程序的运行速度(分布式程序运行的速度是由最慢的那个 task 决定的 )

    2. 过多的数据在同一个 task 中运行, 将会把 executor 撑爆

 

九、如何解决spark中的数据倾斜问题

 

  发现数据倾斜的时候,不要急于提高 executor 的资源, 修改参数或是 修改程序,首先要检查数据本身,是否存在异常数据。

  一、数据问题造成的数据倾斜

    1. 找出异常的key

      如果任务长时间卡在最后1(几个)任务, 首先要对key进行抽样分析, 判断是哪些 key 造成的。

      比如: 

        df.select("key").sample(false, 0.1).(k =>(k,1)).reduceBykey(+).map(k =>(k._2, k._1)).sortByKey(false).take(10)

    2. 如果发现多数数据分布都比较平均, 而个别数据比其他数据大上若干个数量级, 则说明发生了数据倾斜。

       经过分析, 倾斜的数据主要有一下三种情况

        1. null(空值)或是一些无意义的信息()之类的, 大多是这个原因引起。

        2. 无效数据, 大量重复的测试数据或是对结果影响不大的有效数据。

        3. 有效数据, 业务导致的正常数据分布。

      解决办法:

        1. 对于第1,2中情况, 直接对数据进行过滤即可(因为该数据对当前业务不会产生影响)。

        2. 第三种情况则需要进行一些特殊操作, 常见的有这几种办法

          (1). 隔离执行, 将异常的key过滤出来单独处理, 最后与正常数据的处理结果进行union操作

          (2). 对key先添加随机值, 进行操作后, 去掉随机值, 再进行一次操作。

          (3). 使用 reduceByKey 代替 groupByKey (reduceByKey用于对每个key对应的多个 value 进行 merge 操  作,最重要的是它能够在本地先进行 merge 操作, 并且 merge 操作可以通过函数自定义。)

          (4). 使用 map join。

         实例操作流程分析:

          •假设说有倾斜的Key,我们给所有的Key加上一个随机数,然后进行reduceByKey操作;此时同一个Key会有不同的随机数前缀,在进行reduceByKey操作的时候原来的一个非常大的倾斜的Key就分而治之变成若干个更小的Key,不过此时结果和原来不一样,怎么破?进行map操作,目的是把随机数前缀去掉,然后再次进行reduceByKey操作。(当然,如果你很无聊,可以再次做随机数前缀),这样我们就可以把原本倾斜的Key通过分而治之方案分散开来,最后又进行了全局聚合

          注意1: 如果此时依旧存在问题, 建议筛选出倾斜的数据单独处理。 最后将这份数据与正常的数据进行union即可。

          注意2: 单独处理异常数据时, 可以配合使用Map Join解决。

  二、spark使用不当造成的数据倾斜。

    1. 提高 shuffle 并行度

      dataFrame 和 sparkSql 可以设置spark.sql.shuffle.partitions 参数控制 shuffle 的并行度,默认为200。

    2. rdd 操作可以设置 spark.default.parallelism 控制并发度,默认参数由不同的Cluster Manager 控制。

      局限性: 只是让每个task执行更少的不同的key。无法解决个别key 特别大的情况在成的倾斜, 如果某些key的大小非常大, 即使一个task 单独执行它, 也会受到数据倾斜的困扰。

    3. 使用map  join  代替 reduce join

      在小表不是特别大(取决于你的executor 大小)的情况下使用, 可以使程序避免shuffle 的过程, 自然也就没有数据倾斜的困扰了。(详情见)

      http://blog.csdn.net/lsshlsw/article/details/50834858

      http://blog.csdn.net/lsshlsw/article/details/48694893

      局限性: 因为是将小数据发送到每个executor上, 所以数据量不能太大。

 

posted @ 2018-12-10 13:34  零下-八度  阅读(123)  评论(0编辑  收藏  举报