概念
RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。
跨界点
磁盘和内存
数据大小可伸缩
容错
不可变的
属性:
分区 一块代表split数据,有分区函数
分区器:哈希分区,范围分区
依赖 通过算子转换
就近原则:因为存在不同节点上
-
Spark主要编程接口是弹性分布式数据集,即RDD,如果在spark程序运行在HDFS上,RDD的本质是一个HDFS上的文件
-
RDD,弹性分布式数据集
-
实质是存储在不同节点计算机中的数据集,分布式存储最大的好处就是可以让数据在不同的工作节点上并行存储,以便在需要数据的时候并行运算,从而获得较高的效率
-
RDD从程序角度来说,是一个scala的抽象类,具体由各子类实现,如MappedRDD、ShuffledRDD等子类。Spark将常用的大数据操作都转换成为RDD的子类。
RDD的创建方式
- 1)从外部存储系统中获取
- 2)从父RDD转换得到新的RDD
- 3)调用SparkContext方式的parallelize。将driver上的数据集并行化,转化为分布式RDD
- 4)更改RDD的持久化,通过Cache函数将计算后的RDD缓存在内存中。
RDD分区
-
对于一个RDD来说,分区的多少涉及对RDD进行并行计算的力度,每一个RDD的分区计算都在一个单独的任务中执行
-
可控制shuffle的数据量,减少一些网络传输
-
对于RDD分区而言,用户可以自行指定分区,可以利用RDD成员变量partition返回的partition数组的大小来查询一个RDD被划分的分区数
,最小的分区是两个,保证并行,根据内核的数目(内核的2~4倍) -
分区过小:一个分区对应一个task,任务调度频繁
-
分区过大:并行处理能力下降
-
合理分区:100ms内计算完分区内数据
-
一个分区内的数据,只能代表一个RDD,不能跨RDD,也不能跨机器(节点)
1)mapPartitionsWithIndex
val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7),3)
rdd1.getNumPartitions
def fun(index:Int,iter:Iterator[Int])={
| iter.map(x=>index +" "+x)}
val rdd2=rdd1.mapPartitionsWithIndex(fun)
rdd2.collect()
res3: Array[String] = Array(0 1, 0 2, 1 3, 1 4, 2 5, 2 6, 2 7)
2)sampl采样
val a = sc.parallelize(1 to 1000,3)
val b = a.sample(true,0.05,100) 给了种子,最后是伪随机的结果
b.collect()
res6: Array[Int] = Array(17, 29, 66, 72, 89, 101, 109, 125, 162, 162, 168, 207, 233, 245, 246, 290, 312, 334, 347, 372, 377, 435, 438, 455, 464, 473, 514, 525, 531, 559, 572, 607, 608, 612, 644, 665, 666, 673, 674, 693, 709, 722, 731, 741, 828, 850, 875, 893, 910, 913, 954, 959, 993)
3)keyBy 元素映射成元祖
val rdd1 = sc.parallelize(Array("a","b","c"))
val rdd2=rdd1.keyBy(_.length)
rdd2.collect()
res7: Array[(Int, String)] = Array((1,a), (1,b), (1,c))
4)mapValues
val rdd3 = sc.parallelize(Array("zhangsan","lisi"))
val rdd4 = rdd3.map(x=>(x,x.length)) 拿到tuple类型的RDD
rdd4.collect()
res8: Array[(String, Int)] = Array((zhangsan,8), (lisi,4))
val rdd5=rdd4.mapValues(_+1)
rdd5.collect()
res9: Array[(String, Int)] = Array((zhangsan,9), (lisi,5))
5)groupByKey
算子操作
1)通过已有的scala集合
val rdd = sc.parallelize(Array(3,4,5,6,7))
2)通过已有的RDD集合
val rdd1 = rdd.map((_*10))
只有动作算子执行的时候,才会触发transformation算子
3)利用已有的文件
val rdd2 = sc.textFile("hdfs://centos0:8020/3.txt")
4)集合
sc.makeRDD(Array(9,19,2,3))
5)指定分区
val rdd = sc.parallelize(Array(3,4,5,6,7),4)
6)
Transformation~做记录,懒惰的性质
rdd.map(_*5)
调用action算子,会触发之前的所有算子开始计算
res1.collect()
- RDD编程是可以看做是一个集合,但实际只是一种抽象,
- RDD所有操作的本质就是org.apache.spark.rdd包中的抽象泛型类RDD[T]上所定义的方法
算子
1)map
val rdd0 = sc.parallelize(Array(1,2,34,5))
val rdd1=rdd0.map(x=>x*10)
针对每个一个元素执行传入的函数,得到新的rdd
rdd1.collect() 查看结果
2)filter
val rdd1 = sc.parallelize(Array(1,2,
3,4,5))
val rdd2=rdd1.filter(_<5) 只能传boolean类型表达式
rdd2.collect()
3)flatmap:集合里套集合时需要用flatMap
rdd2.flatMap(1 to _)
map先做成集合,压平后拿出来(去括号)
4)union
val rdd3 = sc.parallelize(1 to 10)
val rdd4=rdd3 union res5
rdd4.collect()
5)intersection 交集
val rdd5 = rdd3 intersection res5
rdd5.collect()
6)去重
val rdd6 = rdd4.distinct()
rdd6.collect()
7)JOIN
val rdd7 = sc.parallelize(Array(("jeff",1000),("jack",200),("leborn",1000)))
val rdd8 = sc.parallelize(Array(("jeffz",1000),("jack",1000),("leborn",1000)))
val rdd9 = rdd7 join rdd8
rdd9.collect
Array[(String, (Int, Int))] = Array((leborn,(1000,1000)), (jack,(200,1000)))
左连接:把左边匹配不上的加到结果集中
val rdd10 = rdd7 leftOuterJoin rdd8
rdd10.collect():
Array[(String, (Int, Option[Int]))] = Array((leborn,(1000,Some(1000))), (jeff,(1000,None)), (jack,(200,Some(1000))))
8)mapPartitions:针对分区操作
rdd11.mapPartitions(iter=>iter.map(_+"char"))
res21.collect:
Array[String] = Array(achar, bchar, cchar)
迭代器是访问分区数据的入口
因为要加载所有数据,会造成内存溢出,但是效率高