环境
spark:2.3.1
java:1.8
Hadoop:2.7.6
前言
个人笔记
概念
在较高层次,每个Spark
应用都是由driver program
(驱动程序)组成,其中驱动程序就是运行用户main
函数并在集群上执行各种并行操作。Spark
提供的主要抽象就是RDD
,它是跨群集节点分区的元素集合,可以并行操作。
RDDs
是在Hadoop
文件系统(或任何其他Hadoop
支持的文件系统)中的文件或者在驱动程序中已有的Scala集合中开始创建的,并对其进行转换。
用户也可以要求Spark
在内存中持久化RDD
,以便在并行操作中有效地重用它。最后一点,RDD
可以自动从故障节点中恢复(个人认为可能这就是取名Resilient
的原因吧!)。
spark第二个抽象是可用于并行操作的共享变量。默认情况下,当spark在不同节点上并行运行一个函数作为一组任务时,它会将该函数中使用到的每个变量的副本发送给每个任务。有时候,变量需要在任务之间或者 任务与驱动程序之间共享。
spark支持两种类型的共享变量:广播变量broadcast variables
,其可以在所有节点的内存中缓存变量值。另一个变量是accumulators
(累加器),其只能添加到变量中去,例如 计数和总和。
spark
是围绕RDD的概念展开的,个人理解,感觉,spark
可能是基于RDD
而开发的(但又不完全对)。
RDD
是可以并行操作的容错元素集合。其全称为Resilient Distributed Datasets
(可复原的分布式数据集)
创建RDDs
有两种方式可以创建RDDs
;
方式一、 Parallelized Collections
在你的驱动程序中,并行化一个现有的集合。
并行化集合通过在驱动程序中的现有集合上调用JavaSparkContext
的并行化方法来创建:
集合中的每个元素会被复制形成一个可以并行操作的分布式数据集。例如下面对数字1到5是如何创建一个并行集合的。
List<Integer> data = Arrays.asList(1, 2, 3, 4, 5);
JavaRDD<Integer> distData = sc.parallelize(data);
一旦创建,分布式数据集(disData)将可以并行操作。例如,我们可以调用distData.reduce((a, b) -> a + b)
来使列表中的元素相加。
并行集合的一个很重要参数是将数据集(dataset
)切成分区数量(有多少个分区,就把数据集分割成多少块)。Spark将为集群中的每个分区运行一个任务。通常,希望集群中的每个CPU使用2-4个分区。通常情况下,spark会参数根据你的集群自动设置分区数量。但是你也可以将其作为第二个参数传入parallelize 来手动设置(例如:sc.parallelize(data, 10)
)。
注意:代码中的一些地方使用术语切片(分区的同义词)来维持向后兼容性。
方式二、External Datasets
在外部存储系统中,引用一个数据集。如共享文件系统,HDFS
,HBase
或提供Hadoop InputFormat
的任何数据源。
Spark
可以从Hadoop
支持的任何存储源创建分布式数据集,包括本地文件系统,HDFS
,Cassandra
,HBase
,Amazon S3
等
Spark支持 文本文件, SequenceFiles
和任何Hadoop inpuFormat
。
Text file RDDs
可以使用SparkContext’s textFile
方法来创建。该方法为该文件提供一个URI
(亦或是机器的本地路径或者是一个 hdfs://, s3a://
,等等)并读取它作为集合中的行。如下:
一旦创建,distFile可以操作数据集。例如,我们可以使用map
和reduce
,将计算所有行加起来的大小:distFile.map(s -> s.length()).reduce((a, b) -> a + b)
。
基础代码
//从外部文件中定义了一个基本的 RDD
JavaRDD<String> lines = sc.textFile("data.txt");
//定义了 lineLengths 作为 map transformation 的结果。
//请注意,由于 laziness(延迟加载)lineLengths 不会被立即计算
JavaRDD<Integer> lineLengths = lines.map(s -> s.length());
//运行reduce,这是一个 action
//此时,Spark 分发计算任务到不同的机器上运行,每台机器都运行 map 的一部分并本地运行 reduce,
//仅仅返回它聚合后的结果给驱动程序
int totalLength = lineLengths.reduce((a, b) -> a + b);
想将RDD持久化到内存:
lineLengths.persist(StorageLevel.MEMORY_ONLY());
将函数传递给Spark
就java
而言,有两种方式
1、将一个实现了接口的实例传递给spark或者使用匿名内部类。
2、使用lambda
使用lambda方式
JavaRDD<String> lines = sc.textFile("data.txt");
JavaRDD<Integer> lineLengths = lines.map(s -> s.length());
int totalLength = lineLengths.reduce((a, b) -> a + b);
使用匿名内部类的方式
JavaRDD<String> lines = sc.textFile("data.txt");
JavaRDD<Integer> lineLengths = lines.map(new Function<String, Integer>() {
public Integer call(String s) { return s.length(); }
});
int totalLength = lineLengths.reduce(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer a, Integer b) { return a + b; }
});
在自己的类中实现相应的接口,再将实例传递进去
class GetLength implements Function<String, Integer> {
public Integer call(String s) { return s.length(); }
}
class Sum implements Function2<Integer, Integer, Integer> {
public Integer call(Integer a, Integer b) { return a + b; }
}
JavaRDD<String> lines = sc.textFile("data.txt");
JavaRDD<Integer> lineLengths = lines.map(new GetLength());
int totalLength = lineLengths.reduce(new Sum());