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上, 所以数据量不能太大。