携手创作,共同成长!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情
一、宽依赖和窄依赖
-
窄依赖(Norrow Dependency)指父RDD的每个分区只被子RDD的一个分区所使用,例如:map、filter等
-
宽依赖(Shuffle Dependency)父RDD的每个分区都可能被子RDD的多个分区使用,例如:groupByKey、reduceByKey
每个父RDD的partition中的数据都可能会传输一部分到一个RDD的每个partition中。此时父RDD与子RDD会产生复杂的关系
二、Stage
- Spark Job会被划分为多个stage,每一个stage是由一组并行的task组成的
- Stage的划分依据就是看是否产生了Shuflle(即宽依赖),遇到一个Shuffle操作就会被划分为前后两个Stage
三、Spark Job三种提交模式
- Standalone模式
- yarn client模式
- yarn cluster模式
四、shuffle机制
4.1、未优化的Hash Based Shuffle
假设一个节点,节点上有2个CPU,上面运行了4个ShuffleMapTask。每个ShufffleMapTask都会为每个Reduce Task创建一份Bucket缓存,以及对应的ShuffleBlockFile磁盘文件。
如果有100个map task。100个result task ,那么本地磁盘会产生10000个文件,磁盘io过多,影响性能
4.2、优化后的Hash Based Shuffle
Spark引入了Consolidtion机制,一个ShuffleMapTask将数据写入ResultTask数量的本地文件中,这个是不变的,但是当下一个ShuffleMapTask运行的时候,可以直接将数据写入之前的ShuffleMapTask的本地文件中,相当于对多个ShuffleMapTask的输出进行了合并,从而减少了本地磁盘文件中文件的数量
此时文件的数量编程了cpu core数量* result task数量,比如每个节点上有2个CPU,有100个result task,那么每个节点上会产生200个文件
4.3、Sort-Based Shuffle
ShuffleMap Task会写入同一个文件
五、checkpoint(类似快照机制)
比如spark任务比较复杂,从初始化rdd开始到最后整个任务完成,有比较多的步骤。比如有超过10个的运算子,而且整个任务运行的时间比较长。比如适合checkpoint
针对Spark Job,如果我们担心某些关键的,在后面会反复使用的RDD,因为节点故障导致数据丢失,那么可以针对该RDD启动checkpoint机制,实现容错和高可用
- 首先调用SparkContext的setCheckpointDir()方法,设置一个容错的文件系统目录(HDFS),然后对RDD调用checkponit()方法
5.1、RDD之checkpoint流程
- SparkContext设置checkpoint目录,用于存放checkponit的数据,对RDD调用checkpoint方法,然后它就会被RDDCheckpointData对象进行管理,此时这个RDD的checkpoint状态会被设置为Initialized
- 待RDD所在的job运行结束,会调用job中最后一个RDD的doCheckpoint方法,该方法沿着RDD的血缘关系向上查找被checkpoint()标记过的RDD,并将其checkpoint状态从Initialized设置为CheckpointInProcess
- 启动一个单独的job,来将血缘关系中标记为CheckpointInProcess的RDD执行checkpoint操作,也就是将其数据写入checkpoint目录
- 将RDD数据写入checkpoint目录之后,会将RDD状态改变为checpointed,并且还会改变RDD的血缘关系,即会清除掉RDD所有依赖的RDD,最后还会设置其父RDD为新创建的checkpointRDD
5.1、checkpoint与持久化的区别
-
lineage是否发生改签
-
丢失数据的可能性
- checkpoint是存储在hdfs中
- 持久化是存储在内存中
- 建议:对需要checkpoint的rdd,先执行persist(StorageLevel.DISK_ONLY)
scala
package com.strivelearn.scala
import org.apache.spark.{SparkConf, SparkContext}
/**
* @author strivelearn
* @version CheckPointOpScala.java, 2022年11月26日
*/
object CheckPointOpScala {
def main(args: Array[String]): Unit = {
//创建SparkContext
val conf = new SparkConf()
conf.setAppName("CheckPointOpScala")
.setMaster("local")
val context = new SparkContext(conf)
//1.设置checkPoint目录
context.setCheckpointDir("hdfs://xxx")
val dataRdd = context.textFile("hdfs://xxx")
//2.对rdd执行checkPoint操作
dataRdd.checkpoint()
dataRdd.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).saveAsTextFile("hdfs://xxx")
context.stop()
}
}
java
package com.strivelearn.java;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import scala.Tuple2;
import java.util.Arrays;
/**
* @author strivelearn
* @version CheckpointOpJava.java, 2022年11月26日
*/
public class CheckpointOpJava {
public static void main(String[] args) {
//1.创建sparkContext
SparkConf sparkConf = new SparkConf();
sparkConf.setAppName("CheckpointOpJava");
sparkConf.setMaster("local");
JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
//1.设置checkpoint目录
javaSparkContext.setCheckpointDir("hdfs://xxx");
JavaRDD<String> stringJavaRDD = javaSparkContext.textFile("hdfs://xxx");
//2.对rdd执行checkpoint操作
stringJavaRDD.checkpoint();
stringJavaRDD.flatMap(line -> Arrays.asList(line.split(" ")).iterator()).mapToPair(word -> new Tuple2<>(word, 1)).reduceByKey((x, y) -> x + y).saveAsTextFile("hdfs://xxx");
}
}
JavaRDD<String> stringJavaRDD = javaSparkContext.textFile("hdfs://xxx")
//开启持久化
.persist(StorageLevel.DISK_ONLY());