目录
1.RDD概念
RDD(Resilibute Distribute Dataset)弹性分布式数据集,是Spark中最基本的的数据处理模型。它是一个抽象类,代表一个弹性的、不可变的、可分区的、是一个元素可并行计算的集合。
1.1 弹性
存储的弹性:内存与磁盘自动切换;
容错的弹性:数据丢失可以自动恢复;
计算的弹性:计算出错需要重试机制;
分片的弹性:可根据需要重新分片;
1.2 分布式
数据存储在大数据集群不同的节点上。
1.3 数据集
RDD封装了计算逻辑,并不保存数据。
1.4 数据抽象
RDD是一个抽象类,需要子类具体实现
1.5 不可变
RDD封装了计算逻辑,是不可变的,想要改变只能产生新的RDD,在新的RDD里封装计算逻辑
1.6 可分区、并行计算
2.RDD核心属性
RDD有5大核心属性。
2.1 分区
RDD数据结构中存在分区列表,用于并行计算,是实现分布式计算的重要属性。
Spark在计算时,使用分区函数对每一个分区进行计算。每个分区的数据可以不同,但是计算逻辑相同。
2.2 依赖关系
多个RDD之间的依赖关系。
2.3 分区器
将数据进行分区。移动数据不如移动计算。
首选位置:判断计算哪一个节点效率最优。
3.执行原理
从计算的角度来说,数据处理需要计算资源(内存)和计算模型(逻辑)。执行时,需要将计算的资源和模型进行协调配合。
spark框架在执行时,先申请资源,然后将应用程序的数据处理逻辑分解成一个个的计算任务,然后分发到已经分配资源的计算节点上,按照指定的计算模型(逻辑)进行数据的计算,最后得到结果。
RDD是Spark框架中用于数据处理的核心模型。
3.1 RDD在Yarn中的工作原理
①首先时启动集群环境,启动了ResourceManager、NodeManager后,就有了计算资源;
②然后Spark申请资源,创建调度节点(Driver)和计算节点(Executor),调度节点(Driver)和计算节点(Executor)都是运行在NodeManager;
③Spark框架根据需求将计算逻辑根据分区划分成不同的任务((Task),然后放到任务池(TaskPool);
④调度节点(Driver)将任务根据计算节点状态发送到对应的计算节点进行计算;
从以上可以看出,RDD主要将逻辑进行封装,生成Task并发送给Executor节点进行计算。
4.编程实现
RDD创建方式分为四种
4.1 从集合(内存)中创建RDD
从集合(内存)中创建RDD,spark主要提供了两个方法:parallelize和makeRDD
object CreateRdd01 {
def main(args: Array[String]): Unit = {
val scf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(scf)
//创建RDD
val seq = Seq[Int](1,2,3,4,5)
val rdd = sc.makeRDD(seq)
//val rdd = sc.parallelize(seq)
rdd.collect().foreach(println(_))
sc.stop()
}
}
4.2 从外部存储(文件)创建RDD
外部存储(文件),是有外部的数据集创建,包括:本地的文件系统、所有的Hdoop支持的数据集,比如HDFS、HBase等。
object CreateRdd02 {
def main(args: Array[String]): Unit = {
val scf = new SparkConf().setMaster("local").setAppName("RDD")
val sc = new SparkContext(scf)
//从文件中创建RDD,将文件中的数据作为处理的数据源;
val rdd = sc.textFile("datas/4.txt")
rdd.collect().foreach(println(_))
sc.stop()
}
}
4.3 路径的问题
①textFile()的路径可以是相对路径,
val rdd = sc.textFile("datas/4.txt")
②也可以是绝对路径,
val rdd = sc.textFile("G:\MyWoekSpease\spring-web\spark\datas\4.txt")
③也可写文件所在的目录名;")
val rdd = sc.textFile("datas")
④还可以使用通配符;
val rdd = sc.textFile("datas/1*.txt")
⑤还可以是分布式存储路径
val rdd = sc.textFile("hdfs://cen128:50070/test.txt")
wholeTextFiles()以文件为单位读取数据,读取的结果是为元组,元组的第一个元素表示文件的路径,第二表示文件的内容;
val rdd = sc.wholeTextFiles("datas")
object CreateRdd03 {
def main(args: Array[String]): Unit = {
val scf = new SparkConf().setMaster("local[*]").setAppName("RDD")
//配置分区数;
scf.set("spark.default.parallelism", "3")
val sc = new SparkContext(scf)
//创建RDD
/*
makeRDD()方法的第二个参数表示分区数量,不写就是默认值:defaultParallelism(默认值),Spark默认从配置对象中获取
参数spark.default.parallelism,如果获取不到,就使用totalCore属性,也就是当前系统的CPU核数为分区数(最大可用的)
*/
//val rdd = sc.makeRDD(List(1,2,3,4,5,6,7),2)
val rdd = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7))
//将处理的数据保存成分区文件
rdd.saveAsTextFile("output4")
//关闭程序;
sc.stop()
}
}
分区文件里面的内容是怎样被分到分区里面的?
例如,List(1,2,3,4,5),指定为三个分区,那么运行后,每个分区的内容是:
分区1:1
分区2:2,3
分区3:4,5
那么为什么是这样的划为,而不是:
分区1:1,2
分区2:3
分区3:4,5
或者:
分区1:1,2
分区2:3,4
分区3:5
从源码可以得出结论;
如果有三个分区,把分区数看成是一个list(0,1,2),然后对list进行起始位置和末尾位置计算,计算的方式:
(0 until numSlices).iterator.map { i =>
val start = ((i*length)/numSlices)).ToInt
val end = ((i + 1)* length)//numSlices).ToInt
(start,end)
}
这里的i就是list的元素,numSlices是分区数。(start,end)
4.4 从其他的RDD创建
主要是通过一个RDD的运算完后,再产生新的RDD
4.5 直接创建RDD
使用new的方式直接构造RDD,一般由Spark框架自身使用。
--------------------------------------未完待续。。。