大规模数据处理13:弹性分布式数据集RDD
Spark最基本的数据抽象是弹性分布式数据集RDD,Spark基于RDD定义了很多数据操作,从而使得数据处理的代码十分简洁、高效。所以,要想深入学习Spark,我们必须首先理解RDD的设计思想和特性。
RDD就是一个基于分布式内存的数据抽象,它不仅支持基于工作集的应用,同时具有数据流模型的特点。
RDD表示已被分区、不可变的,并能够被并行操作的数据集合。
1.1 分区:分区代表同一个RDD包含的数据被存储在系统的不同节点中,这也是它可以被并行处理的前提,逻辑上可以认为RDD是一个大的数组,数组中的每个元素代表一个分区,在物理存储中,每个分区指向一个存放在内存或硬盘中的数据块,而这些数据块是独立的,他们可以被存放在系统中的不同节点。
RDD中的每个分区存有它在该RDD中的index,通过RDD的ID和分区的index可以唯一确定对应数据块的编号,从而通过底层存储层的接口中提取到数据进行处理。
1.2 不可变性:代表每一个RDD都是只读的,它所包含的分区信息不可以被改变,既然已有的RDD不可以被改变,我们只可以对现有的RDD进行转换操作,得到新的RDD作为中间计算的结果,从某种程度上讲,RDD与函数式编程的Collection很相似。
1.3 并行操作:由于打个RDD的分区特性,使得它天然支持并行操作,即不同节点上的数据可以被分别处理,然后产生一个新的RDD
2.RDD的结构:
RDD里都会包含分区信息、所依赖的父RDD以及通过怎样的转换操作才能由父RDD得来信息。
2.1 依赖关系:
Dependencies是RDD中最重要的组件之一。如前文所说,Spark不需要将每个中间计算结果进行数据复制以防数据丢失,因为每一步产生的RDD里都会存储父RDD的分区和子RDD的分区之间是否是一对一的对应关系呢?Spark支持两种依赖关系:窄依赖(Narrow Dependency)和宽依赖(Wide Dependency)。它的依赖关系,即它是通过哪个RDD经过哪个转换操作得到的。窄依赖就是父RDD的分区可以一一对应到子RDD的分区,宽依赖就是父RDD的每个分区可以被多个子RDD的分区使用。
显然,窄依赖允许子RDD的每个分区可以被并行处理产生,而宽依赖则必须等父RDD的所有分区都被计算好之后才能开始处理。
如上图所示,一些转换操作如map、filter会产生窄依赖关系,而Join、groupBy则会生成宽依赖关系。
这很容易理解,因为map是将分区里的每一个元素通过计算转化为另一个元素,一个分区里的数据不会跑到两个不同的分区。而groupBy则要将拥有所有分区里有相同Key的元素放到同一个目标分区,而每一个父分区都可能包含各种Key的元素,所以它可能被任意一个子分区所依赖。