Spark RDD

二、掌握spark RDD的概念、算子的作用和使用(包括创建和各种转换运算,具体到代码的编写使用),不同共享变量的作用和使用、对于RDD的依赖关系要理解,知道持久化的方法以及类型;

1、Spark RDD的概念

RDD是可扩展的弹性分布式数据集(一种容错的并行数据结构);

是只读、分区且不变的数据集合;

是Spark的基石,也是Spark的灵魂;

是一种分布式的内存抽象,不具备Schema的数据结构(可以基于任何数据结构创建,如tuple(元组)、dict(字典)和list(列表))

RDD的五个主要属性:

(1)分区信息(Partition)(2)自定义分片计算(3)RDD之间相互依赖(4)控制分片数量(5)使用列表方式进行块存储

4、共享变量

(1)累加器(Accumulator)

累加器是一个全局的共享变量,累加器可以很好地解决上述程序的闭包问题。使用累加器完成相同的功能,代码如下:

sum = sc.accumulator(0)  # 创建一个累加器,初值为0
def fn1(x):
    global sum
    sum += x  # 注意这里不能是 sum=sum+x,因为+=是原地操作,+是需要两个变量类型一致。
a_rdd = sc.parallelize([1, 2, 3, 4, 5])
a_rdd.foreach(fn1)
print(sum.value)  # sum.value可以获取累加器的值,此时打印输出的是15

累加器是一个write-only的变量,工作节点worker中的task无法读取这个值,只能在驱动程序中使用value方法来读取累加器的值。

(2)广播变量(Broadcast)

广播变量和累加器类似,也是一个共享变量,广播变量能够以一种更有效率的方式将一个大数据量输入集合的副本分配给每个节点。

SparkContext对象的broadcast方法可以创建广播变量,广播变量的value属性可以获取该广播变量的值,unpersist方法可以在执行程序上删除此广播的缓存副本。destroy方法可以销毁广播变量,一旦广播变量被销毁,就不能再使用了。

>>> b = sc.broadcast(10)  # 创建一个广播对象
>>> b.value  # 获取广播对象的值
10
>>> sc.parallelize([1, 2, 3, 4, 5]).map(lambda x: x * b.value).collect()
[10, 20, 30, 40, 50] 
>>> b.destroy()  # 销毁广播变量,销毁后就不能访问它的value了
>>> b.value  #  但是pyspark中还是能访问到这个值,这是pyspark的问题,如果是scala确实是无法访问它的值了
10
>>> sc.parallelize([1, 2, 3, 4, 5]).map(lambda x: x * b.value).collect()  # task中确实无法访问该广播变量的值了

5、依赖问题

(1)RDD只能基于在稳定物理存储中的数据集和其他已有的RDD上执行确定性操作来创建。
(2)RDD在血统依赖方面,分为窄依赖和宽依赖。他们用来解决数据容错的高效性。

窄依赖:
一个父RDD的分区partition最多被子RDD的一个分区使用(独生子女)。
在一个集群节点上管道式执行。
比如map、filter、union等;

宽依赖:
多个子RDD的Partition会依赖同一个父RDD的Partition,会引起shuffle(超生)。
比如groupByKey、reduceByKey、 sortBy、partitionBy等;

注意:一个RDD对不同的父节点可能有不同的依赖方式,可能对父节点1是宽依赖,对父节点2是窄依赖。

shuffle:Spark 里的某些操作会触发 shuffle,shuffle 是spark 重新分配数据的一种机制,使得这些数据可以跨不同的区域进行分组。

DAG:Spark里的每一个转换操作都会生成一个新的RDD,RDD之间连一条边,最后这些RDD和他们之间的边组成一个有向无环图DAG(Directed Acyclic Graph)。

一个Stage的开始就是从外部存储或者shuffle结果中读取数据;一个Stage的结束就是发生shuffle或者生成结果时。

spark划分stage的整体思路是:从后往前推,遇到宽依赖就断开,划分为一个stage;遇到窄依赖就将这个RDD加入该stage中。

6、RDD的持久化

可以使用persist方法和cache方法,cache方法只能缓存在内存中, persist方法可以缓存在磁盘上或者内存中。

is_cached属性可以查看当前RDD的持久化状态,或者使用getStorageLevel方法获取当前RDD的持久化状态,unpersist方法可以解除RDD的持久化

StorageLevel****类型 类型描述 对应的****useDisk, useMemory, deserialized, off_heap, replication
MEMORY_ONLY (默认级别)将RDD以JAVA对象的形式保存到JVM内存。如果分片太大,内存缓存不下,就不缓存 StorageLevel(False, True, False, False, 1)
MEMORY_ONLY_2 (默认级别)将RDD以JAVA对象的形式保存到JVM内存。如果分片太大,内存缓存不下,就不缓存,将分区复制到两个集群节点上 StorageLevel(False, True, False, False, 2)
MEMORY_ONLY_SER 将RDD以序列化的JAVA对象形式保存到内存 StorageLevel(False, True, False, False, 1)
MEMORY_ONLY_SER_2 将RDD以序列化的JAVA对象形式保存到内存,将分区复制到两个集群节点上 StorageLevel(False, True, False, False, 2)
DISK_ONLY 将RDD持久化到硬盘 StorageLevel(True, False, False, False, 1)
DISK_ONLY_2 将RDD持久化到硬盘,将分区复制到两个集群节点上 StorageLevel(True, False, False, False, 2)
MEMORY_AND_DISK 将RDD数据集以JAVA对象的形式保存到JVM内存中,如果分片太大不能保存到内存中,则保存到磁盘上,下次用时重新从磁盘读取 StorageLevel(True, True, False, False, 1)
MEMORY_AND_DISK_2 将RDD数据集以JAVA对象的形式保存到JVM内存中,如果分片太大不能保存到内存中,则保存到磁盘上,下次用时重新从磁盘读取,并将分区复制到两个集群节点上 StorageLevel(True, True, False, False, 2)
MEMORY_AND_DISK_SER 与MEMORY_ONLY_SER类似,但当分片太大,不能保存到内存中,会将其保存到磁盘中 StorageLevel(True, True, False, False, 1)
MEMORY_AND_DISK_SER_2 与MEMORY_ONLY_SER类似,但当分片太大,不能保存到内存中,会将其保存到磁盘中,将分区复制到两个集群节点上 StorageLevel(True, True, False, False, 2)
OFF_HEAP 是否利用java unsafe API实现的内存管理,RDD实际被保存到Tachyon StorageLevel(True, True, True, False, 1)
检查点 checkpoint

通过cache或者persist将RDD持久化到内存或者磁盘中,这样做并不能保证数据完全不会丢失,当数据丢失的时候,Spark会根据RDD的计算流程DGA重新计算一遍,这样子就很费性能,

checkpoint的作用就是将DAG中比较重要的中间数据做一个检查点将结果存储到一个高可用的地方(通常这个地方就是HDFS里面,当然也可以是本地文件系统)。

posted @ 2022-04-08 20:02  星月故里yw  阅读(40)  评论(0)    收藏  举报