在高级别上,可以对RDD应用两种转换,即窄转换和宽转换。宽转换基本上会导致阶段边界。
窄转换 - 不需要将数据在分区之间进行洗牌。例如,map
,filter
等。
宽转换 - 需要对数据进行洗牌,例如,reduceByKey
等。
让我们举一个例子,统计每个严重级别的日志消息出现的次数。
以下是以严重级别开头的日志文件:
INFO I'm Info message
WARN I'm a Warn message
INFO I'm another Info message
并创建以下Scala代码以提取相同内容:
val input = sc.textFile("log.txt")
val splitedLines = input.map(line => line.split(" "))
.map(words => (words(0), 1))
.reduceByKey{(a,b) => a + b}
val b = a.map()
时,RDD b
会保留对其父级 a
的引用,这就是血统。toDebugString()
。例如,在splitedLines
RDD上执行toDebugString()
,将输出以下内容:(2) ShuffledRDD[6] at reduceByKey at <console>:25 []
+-(2) MapPartitionsRDD[5] at map at <console>:24 []
| MapPartitionsRDD[4] at map at <console>:23 []
| log.txt MapPartitionsRDD[1] at textFile at <console>:21 []
| log.txt HadoopRDD[0] at textFile at <console>:21 []
sc.textFile()
创建了这个RDD。请参见下面更详细的DAG图形,该图形是从给定的RDD创建的。
textFile
中存在的分区数。例如,在此示例中考虑有4个分区,则将创建4组任务并并行提交,前提是有足够的从节点/核心。下图更详细地说明了这一点:
如果您需要更详细的信息,我建议您观看以下YouTube视频,其中Spark的创建者深入介绍了DAG、执行计划和生命周期。
作业:一段代码,从 HDFS 或本地读取一些输入数据,对数据执行一些计算,并写入一些输出数据。
阶段:作业被划分为多个阶段。阶段被分类为 Map 阶段或 Reduce 阶段(如果您曾经使用过 Hadoop 并且想进行关联,则更容易理解)。阶段基于计算边界进行划分,所有计算(运算符)不能在单个阶段中更新。这需要经过多个阶段来完成。
任务:每个阶段都有一些任务,每个分区一个任务。一个任务在一个执行器(机器)上执行一个数据分区。
DAG:DAG 指的是有向无环图,在当前上下文中,它是运算符的 DAG。
执行器:负责执行任务的进程。
驱动程序:负责在 Spark 引擎上运行作业的程序/进程。
主机:驱动程序运行的机器。
从机:执行器程序运行的机器。
Spark中的所有作业都由一系列操作符组成,并在一组数据上运行。作业中的所有操作符都用于构建DAG(有向无环图)。 DAG通过重新排列和组合操作符进行优化。例如,假设您必须提交一个包含映射操作后跟过滤操作的Spark作业。 Spark DAG优化器会重新排列这些操作符的顺序,因为过滤将减少需要执行映射操作的记录数。
Spark的代码库很小,系统分为各种层次。每个层次都有一些责任。这些层次彼此独立。
Spark缓存要处理的数据,使其比Hadoop快100倍。Spark高度可配置,并且能够利用已经存在于Hadoop生态系统中的现有组件。这使得Spark呈指数增长,在很短的时间内,许多组织已经在生产中使用它。