- RDD类型:
1. 并行集合(Parallelized Collections): 来自于分布式化的数据对象,比如用户自己键入的数据
2. 文件系统数据集: Hadoop Datasets 或文本文件,比如通过SparkContext.textFile()读取的数据,括号里面的参数是大数据文件读取的路径。这个路劲可以使用某一个文件目录下面的一部分文件,那么就需要通配符匹配这部分文件,如何匹配呢?看下面这个例子:
load_path_om = ''mdfs://cloudhdfs/newschannel/data/ryanxltao/news_streaming/cms_news/
list_dt = ["20200617*", "20200618*", "20200619*", "20200620*", \
"20200621*", "20200622*", "20200623*"]
rdd = sc.textFile(load_path_om + '{' + ','.join(list_dt) + '}' + "/part*", use_unicode=False)
# *就是通配符,表示匹配所有
- 因为RDD的俩种不同类型,所以我们使用文件有不同方式(下面2点和上面2点分别对应)
1. 并行化集合是通过调用SparkContext的parallelize方法,再一个已经存在的数据集合上创建的一个Seq对象
演示:
rdd = sc.parallelize([1,2,3,4,5,6])
rdd.collect()
--> [1,2,3,4,5,6]
2. 文件系统数据集
Spark可以将任何hadoop所支持的存储资源转换成RDD,比如 HDFS.mongodb,HBase,Amazon S3等,Spark支持文本文件,SequenceFIles 和任何Hadoop InputFormat 格式.这里采用textFile来加载一个文件创建RDD:
textFile的参数是一个path,这个path可以是:
1. 一个文件路径,这时候只装载指定的文件
2. 一个目录路径,这时候只装载指定目录下面的所有文件(不包括子目录下面的文件)
3. 通过通配符的形式加载多个文件或者加载多个目录下面的所有文件
演示:
sc = SparkContext()
rows = sc.textFile('/user/hadoop/hello.txt', use_unicode=False)
rows = sc.textFile('/user/hadoop/*')
默认是从hdfs读取文件,也可以指定sc.textFile("路径").在路径前面加上hdfs://表示从hdfs文件系统上读
本地文件读取 sc.textFile("路径").在路径前面加上file:// 表示从本地文件系统读,如file:///home/user/spark/hello.txt
- RDD的计算方式(俩类算子):
1. 变换(Transformations):
特点: 懒执行,变换只是一些指令集并不会去马上执行,需要等到有Actions操作的时候才会真正的据算结果
比如: map() filter() flatMap() groupByKey() reduceByKey()
2. 操作(Actions):
特点: 立即执行
比如: count() take() collect() top() first()
默认情况下使用Action在RDD上时Spark 会重新计算刷新RDD.但是这俩种持久化方法可以将RDD放在内存当中,这样第二次使用的时候action在RDD上时候Spark 不会重新计算刷新RDD
演示:
rows = sc.textFile('/user/hadoop/hello.txt')
rows.persist() # 或者 rows.cache()
rows.count() # 第一次执行,会将RDD放在内存上
rows.count() # 第二次执行不会重新从文件读取RDD
map()与flatMap():
从下面的例子可以看出来:
map()是将文件每一行进行操作,数量不会改变
flatMap()是将所有元素进行操作,数量只会大于或者等于初始数量
filter():
过滤,将符合条件的数据留下来
reduce()与reduceByKey():
1. reduce将RDD中元素前两个传给输入函数,产生一个新的return值,新产生的return值与RDD中下一个元素(第三个元素)组成两个元素,再被传给输入函数,直到最后只有一个值为止。
2. reduceByKey就是对元素为键值对的RDD中Key相同的元素的Value进行reduce操作,因此,Key相同的多个元素的值被reduce为一个值,然后与原RDD中的Key组成一个新的键值对。(去键重)
count()与countByValue():
count() 是用来统计这个RDD文件里面有多少个元素
countByValue() 是用于统计RDD键值对中每个键的数量
aggregate():
它有三个参数,分别是zeroValue初始值,seqOp的操作是遍历分区中的所有元素,combOp操作是把各分区聚合的结果,再聚合。
输出结果 第一个是聚合后的结果相当于reduce,但是是将不同分区的数据进行reduce,再整体镜像reduce
第二个是它操作的次数,操作次数与分区有关系每一个分区进行一次x[1]+1,再加上初始值+1,最后汇总+1,下图显示的结果为(36,6),即为进行了6次操作,4个分区1个初始值1个汇总
# 默认的处理器的数有关,处理器为多少就分几个区,可以用repartition()更改风区数量
# 数据在不同的分区中,通过哈希算法来存储
面试题: 为什么会发生数据倾斜
因为数据存储是通过哈希算法进行存储的,所以可能会发生多个数据存储在相同的分区上,可以通过repartition修改分区来解决
- RDD基本转换操作:
union(): 该函数就是将两个RDD进行合并,不去重
intersection(): 该函数返回两个RDD的交集,并且去重
subtract(): 返回在a中出现,并且不在b中出现的元素,不去重
Cartesian(): 该函数返回的是Pair类型的RDD,计算结果是当前RDD和other RDD中每个元素进行笛卡儿计算的结果。最后返回的是CartesianRDD。(笛卡儿计算是很恐怖的,它会迅速消耗大量的内存,所以要慎用)
- 关于键值对类型的转换操作
pathA = [('a',1),('b',1),('c',2),('d',3)]
pathB = [('c',1),('d',3),('e',3),('f',4),]
a = sc.parallelize(pathA)
b = sc.parallelize(pathB)
print(a.join(b).collect()) # 交集
print(a.rightOuterJoin(b).collect()) # 右连接
print(a.leftOuterJoin(b).collect()) # 左连接
print(a.cogroup(b).collect()) # 全连接
print(a.subtractByKey(b).collect()) # 减连接
- RDD元素取值操作:
take(n) 返回前n个元素
top(n) 返回最大的n个元素
first() 返回第一个元素
collect() 返回所有元素,一般元素少的话才会使用
lookup(key) 返回某键值下的所有值
collectAsMap()返回的是一MAP形式的串行化结果
countByKey() 返回的是每一键组内的记录数
例题:
1. 一个班级中男生女生各多少
2. 各学历水平的平均收入
3. 各季节的最高和最低温度
建立键值对RDD:
zip() 合并俩个列表,并按索引对应值
rdd.keys() 返回的是键
rdd.values() 返回的是值
怎么样将计算结果保存到文件中:
累加器和广播变量
累加器是仅仅被相关操作累加的变量,因此可以在并行中被有效地支持。
广播变量允许程序员将一个只读的变量缓存在每台机器上,而不用在任务之间传递变量。