Spark的基础知识
Spark中最核心的是什么,是RDD
那什么是RDD,虽然spark官网也给出了一些解释,但是还是源码是根本,所以我们还是从源码来看
所以,这里我贴出官方GitHub上的RDD源码来一探究竟:
https://github.com/apache/spark/blob/master/core/src/main/scala/org/apache/spark/rdd/RDD.scala
这是我再IDEA中打开的源码介绍:
A Resilient Distributed Dataset (RDD), the basic abstraction in Spark. Represents an immutable,
partitioned collection of elements that can be operated on in parallel.
这句话是rdd这个抽象类的开篇的注释,在很多大厂的面试都会问到,所以请大家好好阅读一下。
RDD是一个弹性分布式数据集,是Spark的基本抽象,其代表着一个不可变的,分区数据集合,而且这个集合可以被并行的计算。那么我们怎么了解这段话呢?我们接着看注释里面的解释,rdd有5个主要的特性很好地解释了这么一段话。
A list of partitions
A function for computing each split
A list of dependencies on other RDDs
Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
Optionally, a list of preferred locations to compute each split on (e.g. block locations for
an HDFS file)
我们来一条条地解读一下:
A list of partitions
这个是一系列的分区,这就是说当一个rdd存储着一系列的数据时,它会构建多个分区来存储这些数据,而不同的分区会在不同的地址(rdd是一个分布式的数据集合),而且在Spark中一个数据集有多少个partition在计算的过程当中就会有多少个task的。
A function for computing each split
一个函数的计算将会作用到每一个分片上(这里的分片就是分区的意思)。在程序上我们是对一个rdd进行一个函数操作,例如:RDD_A.map(…),其实是对每一个分片都做一个map操作。这样,无论你是单机操作还是分布式操作所码的代码不都是一样的吗,底层我们就看不到了。
A list of dependencies on other RDDs
一系列得依赖,上面的那句话提示过我们,rdd是不可变的集合,就是当你对一个rdd进行一个计算操作后将会得到一个新的rdd了,这样每一次的操作后得到的rdd都是依赖于原来的rdd的。
Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
(可选项)当我们的rdd元素是一些键值对的时候,其有一个专门的partitioner来对数据进行分区的。比如说,MapReduce的分区规则就是根据key的哈希值取模得到的。
Optionally, a list of preferred locations to compute each split on (e.g. block locations for
an HDFS file)
(可选项)当我们的数据是集群操作的时候,怎么才能做到数据本地化计算,这个就是每个分片的最佳计算位置了。
RDD类体
既然,RDD是一个抽象类,我们来看看其继承子类都要重写那些抽象方法吧。
这里定义了几个抽象方法:
- compute方法,参数传入的是一个Partition类对象和一个TaskContext类对象,将返回一个迭代器。对应着RDD的第二个特点
- getPartitions方法,这个方法就是用来获取RDD的一系列partitions的,返回是一个泛型是Partititon类的数组,对应着RDD的第一个特点
- getDependencies方法,这个是获取RDD的依赖RDD的,放回一个Seq的,对应着第三个特点
- getPreferredLocations这个方法是获取partition的最佳位置的,对应着第五个特点
- partitioner定义了一个可选的分分区器,对应了第四个特点
因此,我们也可以再次看出,RDD的五大特性很重要。
这里引用一下RDD的子类HadoopRdd来看一下子类实现:
可以看出他继承了RDD
里边comput实现:
getPartitions实现:
getPreferredLocations实现:
partitioner实现:
注意,这里并没有getDependencies,因为getDependencies方法需要依赖上一个RDD,然而HadoopRDD是初始RDD并没有上一个RDD所以并没有getDependencies方法。
在idea上开始我们的Spark编程
配置
首先,需要在pom.xml上配置gav:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
如果你是要和HDFS进行交互的,也要把Hadoop的gav给配置好:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
别忘了在开头的properties指明Spark和Hadoop的版本:
<spark.version>2.4.0</spark.version>
<hadoop.version>2.6.0-cdh5.7.0</hadoop.version>
因为这里用到了cdh的配置,你需要在repositories后面配上cdh的仓库,在编译Spark已经给过了。
等maven编译好了以后,我们就可以使用了
SparkConf和SparkContext
要开始一段Spark的编程首先我们肯定需要一个SparkContext这样的一个上下文,然而如果需要这个上下文,我们又要给它配置一个SparkConf的配置类,我们在idea里面看看这两个类的源码是什么?
Main entry point for Spark functionality. A SparkContext represents the connection to a Spark
cluster, and can be used to create RDDs, accumulators and broadcast variables on that cluster.
Only one SparkContext may be active per JVM. You must stop() the active SparkContext before
creating a new one. This limitation may eventually be removed; see SPARK-2243 for more details.
这一段是SparkContext的开篇注释,里面讲到这个类的对象是Spark的主入口点,用于连接到Spark的集群上的,可以创建RDD,计算器和广播变量等,并且一个SparkContext对应一个JVM,所以当我们的程序结束时要关闭这个SparkContext对象。
我们再来看看这个类的一些构造器吧:
我们是要传入一个SparkConf的进入SparkContext的构造器里面的,如果我们没有传入任何参数,构造器将会自动new一个SparkConf对象出来作为参数,这样的设置在提交作业时可以,但如果我们是现在本地测试的话就不是很方便,如果你没有指定AppName和Maser的话是会报错的。
而SparkConf的注释是:Configuration for a Spark application. Used to set various Spark parameters as key-value pairs.
所以这个类其实就是一个用来配置我们Spark作业的一些配置信息的类,我们可以new一个出来然后使用它提供的setXXX方法来设置我们的一些 配置值,具体地请好好阅读源码信息。