#博学谷IT技术支持#
三、RDD的内核调度
3.1 RDD依赖关系
- 窄依赖
定义:一个RDD上一个分区的数据只能完整的交付给下一个RDD的一个分区,不能分隔。
目的:为了实现并行计算操作提高容错能力。
举例:map(),union()
- 宽依赖
定义:上一个RDD的某一个分区的数据被下游的一个RDD的多个分区所接收,中间必然存在shuffle操作,是否存在shuffle是判定宽窄依赖关系的重要依据。一旦有了shuffle操作, 后续的RDD执行必须等待前序RDD执行完成才能执行。
目的:划分stage的依据
举例:reduceByKey()
3.2 DAG和stage
DAG:有向无环图,描述一段执行任务从开始一直往下执行不允许出现回调的操作。
Spark应用程序中,每遇到一个action算子就会触发一个job任务,每个job任务都会产生一个DAG执行流程图。
- 遇到action算子后触发一个job任务,首先会将这个action算子所依赖的所有RDD全部加载形成一个完整的stage阶段。
- 根据RDD之间的宽窄依赖关系,从后往前,如果遇到窄依赖就放置在一起形成一个stage,如果遇到宽依赖就拆分为两个stage,直到完成形成最终的DAG执行流程图。
3.3 RDD的shuffle
-
Hash Shuffle(1.1版本前)
-
优化前:上游RDD的每一个分区对应每一个线程,都会产生与下游分区数量相同的文件数量,导致输出文件数据过多,也导致下游各个分区拉取数据的时候需要拉取的文件数过多,频繁打开和关闭文件。
-
优化后:由原来让线程输出等量分区的文件数变为由executor输出与下游的RDD分区数等量的文件数,降低了文件的产生。
-
-
Sort Shuffle
-
普通机制:每个线程处理后将数据写入内存,当内存数据达到阈值后触发溢写操作,在一些时候需要对数据进行分区/排序操作,将数据写入到磁盘上,当整个溢写完成后将多个小文件合并为一个大文件,同时为这个大文件提供一个索引文件,方便下游读取对应分区的数据。
-
bypass机制:去除了排序操作,要求上游分区数量不能超过200个,上游不能进行提前聚合操作。
-
3.4 Job的调度流程
Drive内部如何调度任务
- 程序启动后首先创建SparkContext对象,底层创建DAGScheduler和TaskScheduler。
- 遇到action算子后产生一个job任务,SparkContext对象将任务提交到DAGScheduler,DAGScheduler接收到任务后产生一个DAG执行流程图,划分stage,确定每个stage中需要运行多少个线程,将每个stage的线程放置到一个TaskSet集合中交给TaskScheduler。
- TaskScheduler接收到TaskSet后确定每个线程应该运行在哪个executor上,然后将任务提交给executor,让executor启动线程执行任务,stage是一个个运行无法并行执行。
- Driver负责监听各个executor执行状态。
3.5 并行度
- 资源的并行度:executor数量和CPU核心数以及内存大小。
- 数据的并行度:Task的线程数和分区数。
- 推荐一个CPU运行2~3个线程,一个CPU配置3~5G内存,一个线程处理1~3G数据,处理时间10~20分钟。
- 设置并行度:SparkConf().set('spark.default.parallelism', '10'),shuffle后分区数就是10,shuffle前分区数不受这个参数影响。