spark的核心RDD
1.RDD是什么?(5个特性)
- RDD是由一系列partition组成
- 算子是作用与partition上的(而非RDD)
- RDD之间有依赖关系
- 分区器是作用在K-V格式的RDD上
- partition对外提供最佳计算位置,利于数据处理的本地化
2.RDD的理解
- hdfs有多个block块(128M),它是hdfs最小的存储单元,RDD由partition构成,partition分布在多个节点上,一个partition的大小与bolck的大小一致,都是128M
- partition是RDD的最小单元
- 默认情况下,block数对应partition数
- 算子作用于每个partition产生子partition(而不是作用在RDD上)
- RDD中分区个数与父RDD的分区个数一致
- RDD是抽象的,partition是具体的,两者均不存储数据
- RDD间有依赖关系,丢失了一个可以恢复
- 分区器只能作用与K-V格式的RDD(K-V格式的RDD即RDD中每个元素是二元组Tuple2,分区器决定数据去往那个分区,shuffle操作有用分区器)
- 一个partition只能由一个task来处理(可通过增加分区,来提高并行度)「一个partition对应一个task,一个core只能处理一个task」
- 一个partition分区(A机器节点上,分区p1)通过算子作用(如map)后,新的RDD对应的分区(P1_)仍然在该机器上(无shuffle时),有shuffle时,上一个分区p1就会被分为多个分区,散落在下一个RDD的各个分区上【M/R是有shuffle的,其内默认使用的是hash分区器】
- shuffle操作缺点:大量磁盘I/O,网络传输
3.Transformation与Action算子
- Master/Driver/Worker都是JVM进程
- Driver作用:向Worker发送task任务,并回收执行结果。每个Application都有一个Driver
- Transformation算子:懒加载,延迟执行,如flatmap/map/reduceByKey算子
- Action算子:触发执行,触发Transformation算子去执行,如foreach算子
4. 问题
- 什么是K-V格式的RDD?
– RDD中的每个元素是一个个的二元组,那么这个RDD就是K,V格式的RDD
- sc.textFile()方法
– spark没有直接读取hdfs文件的方法,textfile()底层调用的是M/R读取hdfs文件的方法,首先会split,每个split默认大小为128M,就是一个block大小,每个split对应一个partition
- 哪里体现了RDD的弹性(容错)?
– RDD partition的个数可多可少
– RDD之间有依赖关系
- 哪里体现了RDD的分布式?
– RDD中的partition分布在多个节点上
5. 算子练习
- 1.Transformation算子:RDD1->RDD2
(1) flatmap 一对多
(2) map 一对一
(3) filter
(4) sortBy / soertByKey
- 2.Action算子:RDD->非RDD
(1) foreach
(2) first
(3) take(n) 取前n条记录
(4) count
(5) collect 将数据拉取到本地
注意:take 、 collect算子再scala中返回的是Array[String]类型数据,在java中返回的是List类型 - 3.scalaDemo
package com.qiuyang.scala
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkContext, SparkConf}
/**
* Created by qiuyang on 2018/11/29.
* 常用算子练习
* Transformtion算子与Action算子的本质区别在于:
* Transformtion算子: 输入输出是 RDD1->RDD2
* Action算子: 输入输出是 RDD->非RDD(普通数据类型)
*
* 1. Transformation
* sample,以一定概率抽样
* flatmap
* map
* filter
* reduceBy
* reduceByKey
sortBy/sortByKey
*
* 2. Action
* foreach
* first
* take
* count
* collect
*
*/
object TransActionOperator {
def main(args: Array[String]) {
val conf: SparkConf = new SparkConf()
conf.setAppName("scalaOperator").setMaster("local")
val sc: SparkContext = new SparkContext(conf)
val lines: RDD[String] = sc.textFile("/Users/qiuyang/githublib/qiuyang-spark-scala/src/word")
//------------Action算子------------
//1. first算子: 拿到RDD中的第一条记录hello world
// val str: String = lines.first()
// println(str)
//2. take算子: 拿到RDD的前n条记录,封装成数组Array[String]
// val arr: Array[String] = lines.take(10)
// arr.foreach(println)
//3. count算子: 获取RDD中元素的数量Long
// val count: Long = lines.count()
// println(count)
//4.collect算子: 将数据回收到driver端,(存储在java的堆内存中,谨慎使用)Array[String]
// val collect: Array[String] = lines.collect()
// collect.foreach(println)
//5.foreach算子, 普通数组也有foreach函数,此处foreach作用于RDD上(具体说是partition上)
// lines.foreach(println)
//-----------Transformation算子---------
//1. sample算子:
//withReplacement:是否重复取, fraction:取数百分比(大概数目),seed:种子(可选参数)
//withReplacement : scala.Boolean, fraction : scala.Double, seed : scala.Long =
val sample: RDD[String] = lines.sample(true,0.1,1L)
sample.foreach(println)
sc.stop()
}
}
package com.qiuyang.java;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.VoidFunction;
import java.util.List;
public class OperatorDemo {
public static void main(String[] args){
SparkConf conf = new SparkConf().setAppName("operator").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
JavaRDD<String> lines = sc.textFile("/Users/qiuyang/githublib/qiuyang-spark-scala/src/word");
JavaRDD<String> sample = lines.sample(true, 0.1);
sample.foreach(new VoidFunction<String>() {
@Override
public void call(String s) throws Exception {
System.out.println(s);
}
});
sc.stop();
}
}