RDD(Resilient Distributed Datasets,弹性分布式数据集),是Spark最为核心的概念。
RDD的特点:
- 是一个分区的只读记录的集合;
- 一个具有容错机制的特殊集;
- 只能通过在稳定的存储器或其他RDD上的确定性操作(转换)来创建;
- 可以分布在集群的节点上,以函数式操作集合的方式,进行各种并行操作。
Partitioner决定RDD的分区方式Partitioner决定RDD的分区方式
HashPartitioner:以key作为分区条件的散列分布,分区数据不连续,极端情况也可能散列到少数几个分区上,导致数据不均等
RangePartitioner:Range按Key的排序平衡分布,分区内数据连续,大小也相对均等
窄依赖:父RDD的每个分区最多只能被子RDD的一个分区使用
宽依赖:一个父RDD的每个分区可以被子RDD的多个分区使用
Stage:Stage可以简单理解为是由一组RDD组成的可进行优化的执行计划。如果RDD的衍生关系都是窄依赖,则可放在同一个Stage中运行,若RDD的依赖关系为宽依赖,则要划分到不同的Stage。
RDD的创建
makeRDD和parallelize方法:
val conf = new SparkConf().setAppName("Demo").setMaster("local[*]")
val sc = new SparkContext(conf)
val rdd01 = sc.makeRDD(List(1, 2, 3, 4, 5, 6))
val rdd02 = sc.parallelize(List(1, 2, 3, 4, 5, 6), 1)
从文件系统构造
val conf = new SparkConf().setAppName("wordCounts").setMaster("local[*]")
val sc = new SparkContext(conf)
val lines = sc.textFile("file_path")
RDD基本算子
Spark支持两种RDD操作:transformation和action。
transformation操作
transformation操作会针对已有的RDD创建一个新的RDD。transformation具有lazy特性,即transformation不会触发spark程序的执行,它们只是记录了对RDD所做的操作,不会自发的执行。只有执行了一个action,之前的所有transformation才会执行。
常用的transformation介绍:
map :将RDD中的每个元素传人自定义函数,获取一个新的元素,然后用新的元素组成新的RDD。
filter:对RDD中每个元素进行判断,如果返回true则保留,返回false则剔除。
flatMap:与map类似,但是对每个元素都可以返回一个或多个元素。
reduceByKey:对每个key对应的value进行reduce操作。
sortByKey:对每个key对应的value进行排序操作。
join:对两个包含<key,value>对的RDD进行join操作,每个keyjoin上的pair,都会传入自定义函数进行处理。
cogroup:同join,但是每个key对应的Iterable都会传入自定义函数进行处理。
action操作
action操作主要对RDD进行最后的操作,比如遍历,reduce,保存到文件等,并可以返回结果给Driver程序。action操作执行,会触发一个spark job的运行,从而触发这个action之前所有的transformation的执行,这是action的特性。
常用的action介绍:
reduce:将RDD中的所有元素进行聚合操作。第一个和第二个元素聚合,值与第三个元素聚合,值与第四个元素聚合,以此类推。
collect:将RDD中所有元素获取到本地客户端(一般不建议使用)。
count:获取RDD元素总数。
take(n):获取RDD中前n个元素。
saveAsTextFile:将RDD元素保存到文件中,对每个元素调用toString方法。
countByKey:对每个key对应的值进行count计数。
foreach:遍历RDD中的每个元素。
共享变量
Spark提供了两种共享变量:Broadcast Variable(广播变量)和Accumulator(累加变量)。
BroadcastVariable会将使用到的变量,仅仅为每个节点拷贝一份,更大的用处是优化性能,减少网络传输以及内存消耗。广播变量是只读的。
val factor = 3
val broadcastVars = sc.broadcast(factor);
val numberList = Array(1,2,3,4,5)
val number = sc.parallelize(numberList).map( num => num * broadcastVars.value) //广播变量读值broadcastVars.value
Accumulator则可以让多个task共同操作一份变量,主要可以进行累加操作。task只能对Accumulator进行累加操作,不能读取它的值。只有Driver程序可以读取Accumulator的值。
val numberList = Array(1,2,3,4,5)
val numberRDD = sc.parallelize(numberList,1)
val sum = sc.accumulator(0)
numberRDD.foreach{m => sum += m}